#Test Branch created
if(!require("R.matlab")){
  install.packages("R.matlab")
}
if(!require("readxl")){
  install.packages("readxl")
}
if(!require("dplyr")){
  install.packages("dplyr")
}
if(!require("readxl")){
  install.packages("readxl")
}
if(!require("ggplot2")){
  install.packages("ggplot2")
}
if(!require("caret")){
  install.packages("caret")
}
if(!require("glmnet")){
  install.packages("glmnet")
}
if(!require("WeightedROC")){
  install.packages("WeightedROC")
}
if(!require("gbm")){
  install.packages("gbm")
}
if(!require("DMwR")){
  install.packages("DMwR")
}
if(!require("OpenImageR")){
 install.packages("OpenImageR")
}
if(!require("AUC")){
 install.packages("AUC")
}
if(!require("e1071")){
 install.packages("e1071")
}
if(!require("randomForest")){
 install.packages("randomForest")
}
if(!require("xgboost")){
 install.packages("xgboost")
}
if(!require("tibble")){
 install.packages("tibble")
}
if(!require("ROSE")){
 install.packages("ROSE")
}
if(!require("tidyverse")){
 install.packages("tidyverse")
}
if(!require("caTools")){
  install.packages("caTools")
}
if(!require("prediction")){
  install.packages("prediction")
}
if(!require("pROC")){
  install.packages("pROC")
}

library(R.matlab)
library(readxl)
library(dplyr)
library(ggplot2)
library(caret)
library(glmnet)
library(WeightedROC)
library(gbm)
library(DMwR)
### new libraries
library(OpenImageR)
library(AUC)
library(e1071)
library(randomForest)
library(xgboost)
library(tibble)
library(ROSE)
library(tidyverse)
library(caTools)
library(prediction)
library(pROC)

Step 0 set work directories

set.seed(2020)

Provide directories for training images. Training images and Training fiducial points will be in different subfolders.

train_dir <- "../data/train_set/" # This will be modified for different data sets.
train_image_dir <- paste(train_dir, "images/", sep="")
train_pt_dir <- paste(train_dir,  "points/", sep="")
train_label_path <- paste(train_dir, "label.csv", sep="")

Step 1: set up controls for evaluation experiments.

In this chunk, we have a set of controls for the evaluation experiments.

run.presentation.day <- TRUE #presentation day flag. No training. Generate a csv file
run.cv <- TRUE # run cross-validation on the training set
sample.reweight <- TRUE # run sample reweighting in model training
K <- 5  # number of CV folds
run.feature.train <- TRUE # process features for training set
run.test <- TRUE # run evaluation on an independent test set
run.feature.test <- TRUE # process features for test set

# gbm
run.gbm.train <- TRUE # gbm(imroved) is the chosen advanced model
run.gbm.test <- TRUE # gbm(imroved) is the chosen advanced model
gbm.numtrees <- 1000 #number of trees to use in gbm

#features options
run.poly.feature <- TRUE # process poly features
run.add.poly.feature <- TRUE # and poly features to features matrix

# svm
run.svm <- FALSE # svm is the chosen advanced model
model.selection <- TRUE # perform model selection on svm models
run.svm.test <- TRUE # evaluate performance on the test set

# random forest
run.rf <- FALSE # random forest is the chosen advanced model
run.balanced.data <- TRUE # whether or not balance the data
train.random.forest <- FALSE # train random forest model
test.random.forest <- TRUE # test random forest model
tune.random.forest <- FALSE # tune random forest model

# ridge
run.ridge <- FALSE # ridge is the chosen advanced model
alpha <- 0 # ridge regression
train.ridge <- TRUE # train ridge model

# PCA + LDA
run.pca_lda <- FALSE # PCA + LDA is the chosen adcanced model
run.select_PC <- TRUE #run different PCs
run.lda <- TRUE # run lda on the training set
run.pca_lad.test <- TRUE # evaluate performance on the test set

Using cross-validation or independent test set evaluation, we compare the performance of models with different specifications.

Step 2: import data and train-test split

#train-test split
info <- read.csv(train_label_path)
n <- nrow(info) #get number of rows from csv
n_train <- round(n*(4/5), 0) #use 4/5 amount of data for training
train_idx <- sample(info$Index, n_train, replace = F) #grab indexes used for training
test_idx <- setdiff(info$Index, train_idx) # get indexes not used for training

Fiducial points are stored in matlab format. In this step, we read them and store them in a list.

#function to read fiducial points
#input: index
#output: matrix of fiducial points corresponding to the index
n_files <- length(list.files(train_image_dir,'*jpg'))
readMat.matrix <- function(index){
     return(round(readMat(paste0(train_pt_dir, sprintf("%04d", index), ".mat"))[[1]],0))
}

if (run.presentation.day){
  test_idx <- c(1:n_files) #sample(n_files, n_files, replace = F)
  run.gbm.train <- FALSE
  run.feature.train <- FALSE
  run.gbm.test <- TRUE
  run.svm <- TRUE
  model.selection <- FALSE
  run.svm.test <- TRUE
}

#load fiducial points
fiducial_pt_list <- lapply(1:n_files, readMat.matrix)
save(fiducial_pt_list, file="../output/fiducial_pt_list.RData")

Step 3: construct features and responses

Figure1

feature.R is the wrapper for all feature engineering functions and options. The function feature( ) have options that correspond to different scenarios for the project and produces an R object that contains features and responses that are required by all the models that are going to be evaluated later.

source("../lib/feature.R")
tm_feature_train <- NA
gbm_tm_feature_train <- NA
if(run.feature.train){
  tm_feature_train <- system.time(dat_train<-feature(fiducial_pt_list,train_idx, run.poly.feature, run.add.poly.feature))
  gbm_tm_feature_train <- system.time(gbm_dat_train<-feature(fiducial_pt_list,train_idx, FALSE, FALSE))
  save(gbm_dat_train, file="../output/gbm_feature_train.RData")
  save(dat_train, file="../output/feature_train.RData")
}else{
  #load(file="../output/feature_train.RData")
  #load(file="../output/gbm_feature_train.RData")
}

tm_feature_test <- NA
gbm_tm_feature_test <- NA
if(run.feature.test){
  tm_feature_test <- system.time(dat_test <- feature(fiducial_pt_list, test_idx, run.poly.feature, run.add.poly.feature))
  gbm_tm_feature_test <- system.time(gbm_dat_test <- feature(fiducial_pt_list, test_idx, FALSE, FALSE))
  save(gbm_dat_test, file="../output/gbm_feature_test.RData")
  save(dat_test, file="../output/feature_test.RData")
}else{
  load(file="../output/feature_test.RData")
  load(file="../output/gbm_feature_test.RData")
}

Step 4: train classification models with training features and responses; run test on test images

Call the train model and test model from library.

train.R and test.R are wrappers for all model training steps and classification/prediction steps.

source("../lib/train.R") 
source("../lib/test.R")

Baseline Model

——–THIS IS TO SEPARATE EACH MODEL. THIS IS TO SEPARATE EACH MODEL. THIS IS TO SEPARATE EACH MODEL.———-

Advanced Model 1: Improved GBM Model

if (run.gbm.train){
  if (sample.reweight){
    
    gbm_dat_train$label <- as.factor(gbm_dat_train$label)
    dat_train_balanced_gbm <- SMOTE(label ~ ., gbm_dat_train, perc.over = 100, perc.under=200)
    #save(dat_train_balanced_rose, file="../output/balanced_data.RData")
    table(dat_train_balanced_gbm$label)
    
    gbm_tm_train <- system.time(gbm_train <- train_gbm(dat_train_balanced_gbm, s=0.1, K=K, n=gbm.numtrees,w = NULL))
    
  } else {
    gbm_tm_train <- system.time(gbm_train <- train_gbm(gbm_dat_train, s=0.1, K=K, n=gbm.numtrees,w = NULL))
  }
  
  # plot the performance
  best.iter.oob <- gbm.perf(gbm_train,method="OOB")  # returns out-of-bag estimated best number of trees
  print(best.iter.oob)
  best.iter.cv <- gbm.perf(gbm_train,method="cv")   # returns K-fold cv estimate of best number of trees
  print(best.iter.cv)
  
  saveRDS(gbm_train, "../output/gbm_model.rds")
  save(gbm_tm_train, best.iter.cv, file="../output/gbm_outputs.RData")
}
if(run.gbm.test){
  load(file="../output/gbm_outputs.RData")
  gbm_tm_test = NA
  feature_test <- as.matrix(gbm_dat_test[, 1:ncol(gbm_dat_test)-1])
  
  gbm_train <- readRDS("../output/gbm_model.rds")
  gbm_tm_test <- system.time(prob_pred_baseline<-test_gbm(gbm_train,as.data.frame(feature_test),n=best.iter.cv,pred.type = 'response'))
  
  label_pred_baseline <- colnames(prob_pred_baseline)[apply(prob_pred_baseline, 1, which.max)]
}

show gbm accuracy and AUC

if (run.gbm.test){
  load(file="../output/gbm_outputs.RData")
  gbm_accu <- mean(gbm_dat_test$label == label_pred_baseline)
  gbm.auc <- WeightedROC(as.numeric(label_pred_baseline), gbm_dat_test$label)
  gbm_auc = WeightedAUC(gbm.auc)
  cat("Time for constructing gbm training features=", gbm_tm_feature_train[1], "s \n")
  cat("Time for constructing gbm testing features=", gbm_tm_feature_test[1], "s \n")
  cat("The AUC of gbm model is", gbm_auc, ".\n")
  cat("The accuracy of GBM baseline model is", gbm_accu*100, "%.\n")
  cat("Time for training gbm model=", gbm_tm_train[1], "s \n") 
  cat("Time for testing model=", gbm_tm_test[1], "s \n")
}

——–THIS IS TO SEPARATE EACH MODEL. THIS IS TO SEPARATE EACH MODEL. THIS IS TO SEPARATE EACH MODEL.———-

Advanced Model 2: Random Forest

if(run.rf){
  # transfer label column from factor to numeric
  dat_train$label <- as.numeric(dat_train$label)
  dat_test$label <- as.numeric(dat_test$label)

  if(run.balanced.data){
    dat_train_balanced_rose<-ROSE(label~., dat_train,seed=2020)$data
    save(dat_train_balanced_rose, file="../output/balanced_train_data_rose.RData")
    dat_test_balanced_rose <- ROSE(label~., dat_test, seed=2020)$data
    save(dat_test_balanced_rose, file = "../output/balanced_test_data_rose.RData")
  } else {
    load(file = "../output/balanced_train_data_rose.RData")
    load(file = "../output/balanced_test_data_rose.RData")
  }
  table(dat_train_balanced_rose$label)
  table(dat_test_balanced_rose$label)
}
if(run.rf){
  source("../lib/random_forest_old_feature.R")
  if(tune.random.forest){
    time.rf.tune <- system.time(rf.tune <- random_forest_tune(dat_train_balanced_rose))
    save(rf.tune, file="../output/rf_tune.RData")
    save(time.rf.tune, file = "../output/rf_tune_time.RData")
  }else{
    load("../output/rf_tune.RData")
    load("../output/rf_tune_time.RData")
  }
  rf.tune
  time.rf.tune[1]
}

mtry = 308 is the best.

if(run.rf){
  source("../lib/random_forest_old_feature.R")
  if(tune.random.forest){
    # Train 500 trees:
    time.rf.train.tree500 <- system.time(random_forest_fit_500_trees <-
                                           random_forest_train_500(dat_train_balanced_rose,mtry = 308))
    save(random_forest_fit_500_trees, file = "../output/rf_train_500_trees.RData")
    save(time.rf.train.tree500, file = "../output/rf_train_500_trees_time.RData")
    cat("500 tree time", time.rf.train.tree500)
    # Train 1000 trees:
    time.rf.train.tree1000 <- system.time(random_forest_fit_1000_trees <-
                                            random_forest_train_1000(dat_train_balanced_rose,mtry = 308))
    save(random_forest_fit_1000_trees, file = "../output/rf_train_1000_trees.RData")
    save(time.rf.train.tree1000, file = "../output/rf_train_1000_trees_time.RData")
    cat("1000 tree time", time.rf.train.tree1000)
    # Train 1500 trees:
    time.rf.train.tree1500 <- system.time(random_forest_fit_1500_trees <-
                                            random_forest_train_1500(dat_train_balanced_rose,mtry = 308))
    save(random_forest_fit_1500_trees, file = "../output/rf_train_1500_trees.RData")
    save(time.rf.train.tree1500, file = "../output/rf_train_1500_trees_time.RData")
    cat("1500 tree time", time.rf.train.tree1500)
    # Train 2000 trees:
    time.rf.train.tree2000 <- system.time(random_forest_fit_2000_trees <-
                                            random_forest_train_2000(dat_train_balanced_rose,mtry = 308))
    save(random_forest_fit_2000_trees, file = "../output/rf_train_2000_trees.RData")
    save(time.rf.train.tree2000, file = "../output/rf_train_2000_trees_time.RData")
    cat("2000 tree time", time.rf.train.tree2000)
    # Train 2500 trees:
    time.rf.train.tree2500 <- system.time(random_forest_fit_2500_trees <-
                                            random_forest_train_2500(dat_train_balanced_rose,mtry = 308))
    save(random_forest_fit_2500_trees, file = "../output/rf_train_2500_trees.RData")
    save(time.rf.train.tree2500, file = "../output/rf_train_2500_trees_time.RData")
    cat("2500 tree time", time.rf.train.tree2500)

    # Train 10 nodes
    time.rf.train.node10 <- system.time(random_forest_fit_10_nodes <-
                                          random_forest_train_10(dat_train_balanced_rose,mtry = 308))
    save(random_forest_fit_10_nodes, file = "../output/rf_train_10_nodes.RData")
    save(time.rf.train.node10, file = "../output/rf_train_10_nodes_time.RData")
    cat("10 node time", time.rf.train.node10)
    # Train 15 nodes
    time.rf.train.node15 <- system.time(random_forest_fit_15_nodes <-
                                          random_forest_train_15(dat_train_balanced_rose,mtry = 308))
    save(random_forest_fit_15_nodes, file = "../output/rf_train_15_nodes.RData")
    save(time.rf.train.node15, file = "../output/rf_train_15_nodes_time.RData")
    cat("15 node time", time.rf.train.node15)
    # Train 20 nodes
    time.rf.train.node20 <- system.time(random_forest_fit_20_nodes <-
                                          random_forest_train_20(dat_train_balanced_rose,mtry = 308))
    save(random_forest_fit_20_nodes, file = "../output/rf_train_20_nodes.RData")
    save(time.rf.train.node20, file = "../output/rf_train_20_nodes_time.RData")
    cat("20 node time", time.rf.train.node20)
    # Train 25 nodes
    time.rf.train.node25 <- system.time(random_forest_fit_25_nodes <-
                                          random_forest_train_25(dat_train_balanced_rose,mtry = 308))
    save(random_forest_fit_25_nodes, file = "../output/rf_train_25_nodes.RData")
    save(time.rf.train.node25, file = "../output/rf_train_25_nodes_time.RData")
    cat("25 node time", time.rf.train.node25)
    # Train 30 nodes
    time.rf.train.node30 <- system.time(random_forest_fit_30_nodes <-
                                          random_forest_train_30(dat_train_balanced_rose,mtry = 308))
    save(random_forest_fit_30_nodes, file = "../output/rf_train_30_nodes.RData")
    save(time.rf.train.node30, file = "../output/rf_train_30_nodes_time.RData")
    cat("30 node time", time.rf.train.node30)
  } else {
    load("../output/rf_train_500_trees.RData")
    load("../output/rf_train_1000_trees.RData")
    load("../output/rf_train_1500_trees.RData")
    load("../output/rf_train_2000_trees.RData")
    load("../output/rf_train_2500_trees.RData")
    load("../output/rf_train_10_nodes.RData")
    load("../output/rf_train_15_nodes.RData")
    load("../output/rf_train_20_nodes.RData")
    load("../output/rf_train_25_nodes.RData")
    load("../output/rf_train_30_nodes.RData")
  }



  #Error rate of each hyperparameter:



  # Evaluate each hyperparameter


  # Predicted value from 500 trees' model:
  rf_predicted_balanced <- as.numeric(as.vector(random_forest_test(random_forest_fit_500_trees,
                                                                   dat_test_balanced_rose)))
  rf_predicted_imbalanced <- as.numeric(as.vector(random_forest_test(random_forest_fit_500_trees, dat_test)))
  # Evaluate 500 trees:
  rf_accuracy_balanced <- mean(round(rf_predicted_balanced == dat_test_balanced_rose$label))
  tpr.fpr.balanced <- WeightedROC(as.numeric(rf_predicted_balanced),dat_test_balanced_rose$label)
  rf_AUC_balanced <- WeightedAUC(tpr.fpr.balanced)
  rf_accuracy_imbalanced <- mean(round(rf_predicted_imbalanced == dat_test$label))
  tpr.fpr.imbalanced <- WeightedROC(as.numeric(rf_predicted_imbalanced),dat_test$label)
  rf_AUC_imbalanced <- WeightedAUC(tpr.fpr.imbalanced)

  cat("Accuracy(balanced) 500", rf_accuracy_balanced*100,"%.\n")
  cat("AUC(balanced) 500", rf_AUC_balanced,".\n")
  cat("Accuracy(imbalanced) 500", rf_accuracy_imbalanced*100,"%.\n")
  cat("AUC(imbalanced) 500",rf_AUC_imbalanced,".\n")


  # Evaluation from 1000 trees' model:
  rf_predicted_balanced <- as.numeric(as.vector(random_forest_test(random_forest_fit_1000_trees,
                                                                   dat_test_balanced_rose)))
  rf_predicted_imbalanced <- as.numeric(as.vector(random_forest_test(random_forest_fit_1000_trees, dat_test)))

  rf_accuracy_balanced <- mean(round(rf_predicted_balanced == dat_test_balanced_rose$label))
  tpr.fpr.balanced <- WeightedROC(as.numeric(rf_predicted_balanced),dat_test$label)
  rf_AUC_balanced <- WeightedAUC(tpr.fpr.balanced)
  rf_accuracy_imbalanced <- mean(round(rf_predicted_imbalanced == dat_test_balanced_rose$label))
  tpr.fpr.imbalanced <- WeightedROC(as.numeric(rf_predicted_imbalanced),dat_test$label)
  rf_AUC_imbalanced <- WeightedAUC(tpr.fpr.imbalanced)

  cat("Accuracy(balanced) 1000", rf_accuracy_balanced*100,"%.\n")
  cat("AUC(balanced) 1000", rf_AUC_balanced,".\n")
  cat("Accuracy(imbalanced) 1000", rf_accuracy_imbalanced*100,"%.\n")
  cat("AUC(imbalanced) 1000",rf_AUC_imbalanced,".\n")

  # 1500 trees
  rf_predicted_balanced <- as.numeric(as.vector(random_forest_test(random_forest_fit_1500_trees,
                                                                   dat_test_balanced_rose)))
  rf_predicted_imbalanced <- as.numeric(as.vector(random_forest_test(random_forest_fit_1500_trees, dat_test)))

  rf_accuracy_balanced <- mean(round(rf_predicted_balanced == dat_test_balanced_rose$label))
  tpr.fpr.balanced <- WeightedROC(as.numeric(rf_predicted_balanced),dat_test$label)
  rf_AUC_balanced <- WeightedAUC(tpr.fpr.balanced)
  rf_accuracy_imbalanced <- mean(round(rf_predicted_imbalanced == dat_test_balanced_rose$label))
  tpr.fpr.imbalanced <- WeightedROC(as.numeric(rf_predicted_imbalanced),dat_test$label)
  rf_AUC_imbalanced <- WeightedAUC(tpr.fpr.imbalanced)

  cat("Accuracy(balanced) 1500", rf_accuracy_balanced*100,"%.\n")
  cat("AUC(balanced) 1500", rf_AUC_balanced,".\n")
  cat("Accuracy(imbalanced) 1500", rf_accuracy_imbalanced*100,"%.\n")
  cat("AUC(imbalanced) 1500",rf_AUC_imbalanced,".\n")

  #2000 trees
  rf_predicted_balanced <- as.numeric(as.vector(random_forest_test(random_forest_fit_2000_trees,
                                                                   dat_test_balanced_rose)))
  rf_predicted_imbalanced <- as.numeric(as.vector(random_forest_test(random_forest_fit_2000_trees, dat_test)))

  rf_accuracy_balanced <- mean(round(rf_predicted_balanced == dat_test_balanced_rose$label))
  tpr.fpr.balanced <- WeightedROC(as.numeric(rf_predicted_balanced),dat_test$label)
  rf_AUC_balanced <- WeightedAUC(tpr.fpr.balanced)
  rf_accuracy_imbalanced <- mean(round(rf_predicted_imbalanced == dat_test_balanced_rose$label))
  tpr.fpr.imbalanced <- WeightedROC(as.numeric(rf_predicted_imbalanced),dat_test$label)
  rf_AUC_imbalanced <- WeightedAUC(tpr.fpr.imbalanced)

  cat("Accuracy(balanced) 2000", rf_accuracy_balanced*100,"%.\n")
  cat("AUC(balanced) 2000", rf_AUC_balanced,".\n")
  cat("Accuracy(imbalanced) 2000", rf_accuracy_imbalanced*100,"%.\n")
  cat("AUC(imbalanced) 2000",rf_AUC_imbalanced,".\n")

  # 2500 trees
  rf_predicted_balanced <- as.numeric(as.vector(random_forest_test(random_forest_fit_2500_trees,
                                                                   dat_test_balanced_rose)))
  rf_predicted_imbalanced <- as.numeric(as.vector(random_forest_test(random_forest_fit_2500_trees, dat_test)))

  rf_accuracy_balanced <- mean(round(rf_predicted_balanced == dat_test_balanced_rose$label))
  tpr.fpr.balanced <- WeightedROC(as.numeric(rf_predicted_balanced),dat_test$label)
  rf_AUC_balanced <- WeightedAUC(tpr.fpr.balanced)
  rf_accuracy_imbalanced <- mean(round(rf_predicted_imbalanced == dat_test_balanced_rose$label))
  tpr.fpr.imbalanced <- WeightedROC(as.numeric(rf_predicted_imbalanced),dat_test$label)
  rf_AUC_imbalanced <- WeightedAUC(tpr.fpr.imbalanced)

  cat("Accuracy(balanced) 2500", rf_accuracy_balanced*100,"%.\n")
  cat("AUC(balanced) 2500", rf_AUC_balanced,".\n")
  cat("Accuracy(imbalanced) 2500", rf_accuracy_imbalanced*100,"%.\n")
  cat("AUC(imbalanced) 2500",rf_AUC_imbalanced,".\n")

  # 10 nodes
  rf_predicted_balanced <- as.numeric(as.vector(random_forest_test(random_forest_fit_10_nodes,
                                                                   dat_test_balanced_rose)))
  rf_predicted_imbalanced <- as.numeric(as.vector(random_forest_test(random_forest_fit_10_nodes, dat_test)))

  rf_accuracy_balanced <- mean(round(rf_predicted_balanced == dat_test_balanced_rose$label))
  tpr.fpr.balanced <- WeightedROC(as.numeric(rf_predicted_balanced),dat_test$label)
  rf_AUC_balanced <- WeightedAUC(tpr.fpr.balanced)
  rf_accuracy_imbalanced <- mean(round(rf_predicted_imbalanced == dat_test_balanced_rose$label))
  tpr.fpr.imbalanced <- WeightedROC(as.numeric(rf_predicted_imbalanced),dat_test$label)
  rf_AUC_imbalanced <- WeightedAUC(tpr.fpr.imbalanced)

  cat("Accuracy(balanced) 10", rf_accuracy_balanced*100,"%.\n")
  cat("AUC(balanced) 10", rf_AUC_balanced,".\n")
  cat("Accuracy(imbalanced) 10", rf_accuracy_imbalanced*100,"%.\n")
  cat("AUC(imbalanced) 10",rf_AUC_imbalanced,".\n")

  # 15 nodes
  rf_predicted_balanced <- as.numeric(as.vector(random_forest_test(random_forest_fit_15_nodes,
                                                                   dat_test_balanced_rose)))
  rf_predicted_imbalanced <- as.numeric(as.vector(random_forest_test(random_forest_fit_15_nodes, dat_test)))

  rf_accuracy_balanced <- mean(round(rf_predicted_balanced == dat_test_balanced_rose$label))
  tpr.fpr.balanced <- WeightedROC(as.numeric(rf_predicted_balanced),dat_test$label)
  rf_AUC_balanced <- WeightedAUC(tpr.fpr.balanced)
  rf_accuracy_imbalanced <- mean(round(rf_predicted_imbalanced == dat_test_balanced_rose$label))
  tpr.fpr.imbalanced <- WeightedROC(as.numeric(rf_predicted_imbalanced),dat_test$label)
  rf_AUC_imbalanced <- WeightedAUC(tpr.fpr.imbalanced)

  cat("Accuracy(balanced) 15", rf_accuracy_balanced*100,"%.\n")
  cat("AUC(balanced) 15", rf_AUC_balanced,".\n")
  cat("Accuracy(imbalanced) 15", rf_accuracy_imbalanced*100,"%.\n")
  cat("AUC(imbalanced) 15",rf_AUC_imbalanced,".\n")

  # 20 nodes
  rf_predicted_balanced <- as.numeric(as.vector(random_forest_test(random_forest_fit_20_nodes,
                                                                   dat_test_balanced_rose)))
  rf_predicted_imbalanced <- as.numeric(as.vector(random_forest_test(random_forest_fit_20_nodes, dat_test)))

  rf_accuracy_balanced <- mean(round(rf_predicted_balanced == dat_test_balanced_rose$label))
  tpr.fpr.balanced <- WeightedROC(as.numeric(rf_predicted_balanced),dat_test$label)
  rf_AUC_balanced <- WeightedAUC(tpr.fpr.balanced)
  rf_accuracy_imbalanced <- mean(round(rf_predicted_imbalanced == dat_test_balanced_rose$label))
  tpr.fpr.imbalanced <- WeightedROC(as.numeric(rf_predicted_imbalanced),dat_test$label)
  rf_AUC_imbalanced <- WeightedAUC(tpr.fpr.imbalanced)

  cat("Accuracy(balanced) 20", rf_accuracy_balanced*100,"%.\n")
  cat("AUC(balanced) 20", rf_AUC_balanced,".\n")
  cat("Accuracy(imbalanced) 20", rf_accuracy_imbalanced*100,"%.\n")
  cat("AUC(imbalanced) 20",rf_AUC_imbalanced,".\n")

  # 25 nodes
  rf_predicted_balanced <- as.numeric(as.vector(random_forest_test(random_forest_fit_25_nodes,
                                                                   dat_test_balanced_rose)))
  rf_predicted_imbalanced <- as.numeric(as.vector(random_forest_test(random_forest_fit_25_nodes, dat_test)))

  rf_accuracy_balanced <- mean(round(rf_predicted_balanced == dat_test_balanced_rose$label))
  tpr.fpr.balanced <- WeightedROC(as.numeric(rf_predicted_balanced),dat_test$label)
  rf_AUC_balanced <- WeightedAUC(tpr.fpr.balanced)
  rf_accuracy_imbalanced <- mean(round(rf_predicted_imbalanced == dat_test_balanced_rose$label))
  tpr.fpr.imbalanced <- WeightedROC(as.numeric(rf_predicted_imbalanced),dat_test$label)
  rf_AUC_imbalanced <- WeightedAUC(tpr.fpr.imbalanced)

  cat("Accuracy(balanced) 25", rf_accuracy_balanced*100,"%.\n")
  cat("AUC(balanced) 25", rf_AUC_balanced,".\n")
  cat("Accuracy(imbalanced) 25", rf_accuracy_imbalanced*100,"%.\n")
  cat("AUC(imbalanced) 25",rf_AUC_imbalanced,".\n")

  # 30 nodes
  rf_predicted_balanced <- as.numeric(as.vector(random_forest_test(random_forest_fit_30_nodes,
                                                                   dat_test_balanced_rose)))
  rf_predicted_imbalanced <- as.numeric(as.vector(random_forest_test(random_forest_fit_30_nodes, dat_test)))

  rf_accuracy_balanced <- mean(round(rf_predicted_balanced == dat_test_balanced_rose$label))
  tpr.fpr.balanced <- WeightedROC(as.numeric(rf_predicted_balanced),dat_test$label)
  rf_AUC_balanced <- WeightedAUC(tpr.fpr.balanced)
  rf_accuracy_imbalanced <- mean(round(rf_predicted_imbalanced == dat_test_balanced_rose$label))
  tpr.fpr.imbalanced <- WeightedROC(as.numeric(rf_predicted_imbalanced),dat_test$label)
  rf_AUC_imbalanced <- WeightedAUC(tpr.fpr.imbalanced)

  cat("Accuracy(balanced) 30", rf_accuracy_balanced*100,"%.\n")
  cat("AUC(balanced) 30", rf_AUC_balanced,".\n")
  cat("Accuracy(imbalanced) 30", rf_accuracy_imbalanced*100,"%.\n")
  cat("AUC(imbalanced) 30",rf_AUC_imbalanced,".\n")
}

15 Nodes and 2500 trees is the best.

if(run.rf){
  if(train.random.forest){
    time.rf.train.final.balanced <- system.time(random_forest_fit_final_balanced <-
                                                  random_forest_train(dat_train_balanced_rose, 
                                                                      mtry = 308,
                                                                      tree_number = 2000, 
                                                                      node_size = 15))
    save(random_forest_fit_final_balanced, file = "../output/rf_train_final_balanced_old_feature.RData")
    save(time.rf.train.final.balanced, file = "../output/rf_train_final_time_balanced_old_feature.RData")
    time.rf.train.final.imbalanced <- system.time(random_forest_fit_final_imbalanced <-
                                                  random_forest_train(dat_train, 
                                                                      mtry = 308, 
                                                                      tree_number = 1000,
                                                                      node_size = 15))
    save(time.rf.train.final.imbalanced, file = "../output/rf_train_final_time_imbalanced_old_feature.RData")
    save(random_forest_fit_final_imbalanced, file = "../output/rf_train_final_imbalanced_old_feature.RData")
  } else {
    load("../output/rf_train_final_balanced_old_feature.RData")
    load("../output/rf_train_final_time_balanced_old_feature.RData")
    load("../output/rf_train_final_time_imbalanced_old_feature.RData")
    load("../output/rf_train_final_imbalanced_old_feature.RData")
  }
}
if(run.rf){
  # Balanced:
  if(test.random.forest){
  time.rf.test.final.balanced <- system.time(
    rf_predicted_balanced <- as.numeric(as.vector(random_forest_test(random_forest_fit_final_balanced,
                                                                     dat_test_balanced_rose))))
  rf_accuracy_balanced <- mean(round(rf_predicted_balanced == dat_test_balanced_rose$label))
  tpr.fpr.balanced <- WeightedROC(as.numeric(rf_predicted_balanced),dat_test_balanced_rose$label)
  rf_AUC_balanced <- WeightedAUC(tpr.fpr.balanced)

  cat("AUC(balanced): ", rf_AUC_balanced,".\n")
  cat("Accuracy(balanced)", rf_accuracy_balanced*100,"%.\n")
  cat("Training time: ", time.rf.train.final.balanced[1], "s.\n")
  cat("Testing time: ", time.rf.test.final.balanced[1], "s.\n")

  # Imbalanced:
  time.rf.test.final.imbalanced <- system.time(
    rf_predicted_imbalanced <-as.numeric(as.vector(random_forest_test(random_forest_fit_final_imbalanced,
                                                                      dat_test))))
  rf_accuracy_imbalanced <- mean(round(rf_predicted_imbalanced == dat_test$label))
  tpr.fpr.imbalanced <- WeightedROC(as.numeric(rf_predicted_imbalanced),dat_test$label)
  rf_AUC_imbalanced <- WeightedAUC(tpr.fpr.imbalanced)

  cat("AUC(imbalanced): ", rf_AUC_imbalanced,".\n")
  cat("Accuracy(imbalanced)", rf_accuracy_imbalanced*100,"%.\n")
  cat("Training time: ", time.rf.train.final.imbalanced[1], "s.\n")
  cat("Testing time: ", time.rf.test.final.imbalanced[1], "s.\n")
  }
}

——–THIS IS TO SEPARATE EACH MODEL. THIS IS TO SEPARATE EACH MODEL. THIS IS TO SEPARATE EACH MODEL.———-

Advanced Model 3: SVM Model

If the given data set is imbalanced, we apply SMOTE to rebalance the data and use it to train our svm models.

if(run.svm){
  tm_svm_rebalanced_train <- NA
  if(sample.reweight){
    dat_train$label = as.factor(dat_train$label)
    tm_svm_rebalanced_train <- system.time(svm_training_data <- SMOTE(label ~ ., data = dat_train))
    save(tm_svm_rebalanced_train, file="../output/tm_svm_rebalanced_train.RData")
  } else {
    svm_training_data <- dat_train
    tm_svm_rebalanced_train <- tm_feature_train
  }
}

Tuning hyperparameters for both linear and radial basis kernel and select the kernel method that produces the highest AUC and accuracy among the two methods.

Since radial basis kernel has higher accuracy and AUC than linear kernel, we will choose radial basis as our kernel method for training the svm model.

if(run.svm){
  svm_testing_data <- dat_test
  if(run.svm.test){
    ## rbf
    tm_svm_rbf_test <- system.time(svm_rbf_pred <- svm_test(svm_radial_mod, svm_testing_data, FALSE))
    svm_test_accu = mean(round(svm_rbf_pred == svm_testing_data$label))
    tpr.fpr.rbf <- WeightedROC(as.numeric(svm_rbf_pred), svm_testing_data$label)
    svm_test_auc = WeightedAUC(tpr.fpr.rbf)
    save(tm_svm_rbf_test, file="../output/tm_svm_rbf_test.RData")
    
    cat("The accuracy of svm model is", svm_test_accu*100, "%.\n")
    cat("The AUC of svm model is", svm_test_auc, ".\n")
  }
}
The accuracy of svm model is 81.83333 %.
The AUC of svm model is 0.7094988 .
if(run.svm){
  cat("Time for training svm model =", svm.rbf.tm[[3]], "s \n")
  cat("Time for testing svm model=", tm_svm_rbf_test[1], "s \n")
}
Time for training svm model = 523.557 s 
Time for testing svm model= 15.378 s 

——–THIS IS TO SEPARATE EACH MODEL. THIS IS TO SEPARATE EACH MODEL. THIS IS TO SEPARATE EACH MODEL.———-

Advanced Model 4: Ridge Model

if(run.ridge){
  tm_ridge_train <- NA
  if (train.ridge){
    dat_train_rebalanced <- ROSE(label ~ ., data = dat_train, seed=2020)$data
    tm_ridge_train <- system.time(ridge_cv_model<-ridge_train(train_data=dat_train_rebalanced, alpha=alpha, K=K, lambda=lambda))
    save(ridge_cv_model, file="../output/ridge_cv_model.RData")
    save(tm_ridge_train, file="../output/ridge_train_time.RData")
  } else {
    load(file="../output/ridge_cv_model.RData")
    load(file="../output/ridge_train_time.RData")
  }
}
if(run.ridge){
  if (run.cv){
    set.seed(2020)
    dat_train_rebalanced <- ROSE(label ~ ., data = dat_train, seed=2020)$data
    feature_train = as.matrix(dat_train_rebalanced[,-dim(dat_train_rebalanced)[2]])
    label_train = as.integer(dat_train_rebalanced$label)
    ridge_model = cv.glmnet(x=feature_train, y=label_train, alpha=alpha, nfolds=K, lambda=lambda)
    opt_lambda = ridge_model$lambda.min
    save(opt_lambda, file="../output/ridge_optimal_lambda.RData")
  } else {
    load(file="../output/ridge_optimal_lambda.RData")
  }
}
if(run.ridge){
  tm_ridge_test = NA
  if(run.test){
    load("../output/ridge_cv_model.RData")
    dat_test_rebalanced <- ROSE(label ~ ., data = dat_test, seed=2020)$data
    feature_test <- as.matrix(dat_test_rebalanced[, -dim(dat_test_rebalanced)[2]])
    tm_ridge_test <- system.time(label_pred<-as.integer(ridge_test(model=ridge_cv_model, features=feature_test, pred.type = 'class')))
    save(tm_ridge_test, file="../output/ridge_test_time.RData")
  } else{
    load(file="../output/ridge_test_time.RData")
  }
}
if(run.ridge){
  cat("Time for constructing training features=", tm_feature_train[1], "s \n")
  cat("Time for constructing testing features=", tm_feature_test[1], "s \n")
  cat("Time for training ridge model=", tm_ridge_train[1], "s \n") 
  cat("Time for testing ridge model=", tm_ridge_test[1], "s \n")
}
if(run.ridge){
  load("../output/ridge_cv_model.RData")
  dat_test_rebalanced <- ROSE(label ~ ., data = dat_test, seed=2020)$data
  feature_test <- as.matrix(dat_test_rebalanced[, -dim(dat_test_rebalanced)[2]])
  label_pred = as.integer(predict(ridge_cv_model, s=opt_lambda, newx=feature_test, type='class'))
  label_test = as.integer(dat_test_rebalanced$label)
  ridge_accuracy = mean(round(label_test== label_pred))
  cat("The accuracy of the ridge model is", ridge_accuracy*100, "%.\n")
  ridge_AUC = auc(roc(label_pred,label_test))
  cat("The AUC of the ridge model is", ridge_AUC, ".\n")
}

——–THIS IS TO SEPARATE EACH MODEL. THIS IS TO SEPARATE EACH MODEL. THIS IS TO SEPARATE EACH MODEL.———-

Advanced Model 5: PCA + LDA

if(run.pca_lda){
  if(sample.reweight){
    dat_train$label <- as.factor(dat_train$label)
    balanced_train_data <- SMOTE(label~.,data = dat_train)
    save(balanced_train_data, file="../output/feature_balanced_train.RData")
  } else {
    load(balanced_train_data, file="../output/feature_train.RData")
  }
}

Since there are over 6000 features, we implement the PCA method to reduce dimension according to the covariance matrix. We only retain PCs with large variance.

if(run.pca_lda){
  balanced_test_data <- dat_test
  if(run.select_PC){
    #separate the features from label
    dat_train_new <- balanced_train_data[,-dim(balanced_train_data)[2]]
    dat_test_new <- balanced_test_data[,-dim(balanced_test_data)[2]]
    #create a vector contain target number of PCs
    num.pca <- c(10,50,500,1000)
    train_pca <- function(num.pca){
      for(i in 1:length(num.pca)){
        #start time for training the model
        train.model.start = proc.time()
        #run PCA
        pca <- prcomp(dat_train_new)
        #store for each potential PC
        train_pca <- data.frame(pca$x[,1:num.pca[i]], label = balanced_train_data[dim(balanced_train_data)[2]])
        pred_pca <- predict(pca,dat_test_new)
        test_pca <- data.frame(pred_pca[,1:num.pca[i]], label = balanced_test_data[dim(balanced_test_data)[2]])
        #fitting the lda model
        lda_pca <- lda(label ~ ., data = train_pca) 
        #stop time for training the model
        train.model.end = proc.time()
        #start time for testing the model
        test.model.start = proc.time()
        #predict lda model
        lda_pred_pca = predict(lda_pca,test_pca[-dim(test_pca)[2]])
        #end time for testing the model
        test.model.end = proc.time()
        #test accuracy
        test_accuracy=confusionMatrix(lda_pred_pca$class, test_pca$label)$overall[1]
        print(list(l1=train.model.end - train.model.start,
                   l2=test.model.end - test.model.start,
                   l3=test_accuracy))
      }
    }
    train_pca(num.pca)
  }
}

By comparing the training time, test time and accuracy, we use model with 500 PCs.

if(run.pca_lda){
  train.model.start = proc.time()
  if(run.lda.train){
    pca_500 <- prcomp(balanced_train_data[,-dim(balanced_train_data)[2]])
    train_pca_500 <- data.frame(pca_500$x[,1:500], label = balanced_train_data[dim(balanced_train_data)[2]])
    pred_pca_500 <- predict(pca_500,balanced_test_data[,-dim(balanced_test_data)[2]])
    test_pca_500 <- data.frame(pred_pca_500[,1:500], label = balanced_test_data[dim(balanced_test_data)[2]])
    save(train_pca_500, file="../output/feature_pca_train.RData")
    save(test_pca_500, file="../output/feature_pca_test.RData")  
  } else {
    load(train_pca_50, file="../output/feature_pca_train.RData")
    load(test_pca_50, file="../output/feature_pca_test.RData")  
  }
  #calculate the training time
  lda_pca_50 <- lda(label ~ ., data = train_pca_50, cv = TRUE)
  train.model.end = proc.time()
}
if(run.pca_lda){
  test.model.start = proc.time()
  pred_train_lda <- predict(lda_pca_10, train_pca_10[-dim(train_pca_10)[2]])
  accu_train_lda <- mean(pred_train_lda$class == train_pca_10$label)
  cat("The trainig accuracy of model: LDA", "is", accu_train_lda*100, "%.\n")
  #calculating the test time
  if(run.test){
    pred_test_lda <- predict(lda_pca_10, test_pca_10)
  }
  test.model.end = proc.time()
  save(pred_test_lda, file="../output/fit_train.RData")
  accu_test_lda <- mean(pred_test_lda$class == test_pca_10$label)
  cat("The accuracy of model: LDA", "is", accu_test_lda*100, "%.\n")
  tpr.fpr <- WeightedROC(as.numeric(pred_test_lda$class), test_pca_10$label)
  lda_auc = WeightedAUC(tpr.fpr)
  cat("The AUC of model: LDA is", lda_auc, ".\n")
}

Prediction performance matters, so does the running times for constructing features and for training the model, especially when the computation resource is limited.

if(run.pca_lda){
  tm_train <- train.model.end - train.model.start
  tm_test <- test.model.end - test.model.start
  cat("Time for constructing training features=", tm_feature_train[1], "s \n")
  cat("Time for constructing testing features=", tm_feature_test[1], "s \n")
  cat("Time for training model=", tm_train[1], "s \n") 
  cat("Time for testing model=", tm_test[1], "s \n")
}
if (run.presentation.day){
  csvfileoutput<-"../output/label_prediction.csv"
  Advanced<-svm_rbf_pred
  Baseline<-label_pred_baseline
  Index<-test_idx
  csvdata <- data.frame(Index, Baseline, Advanced)
  
  write.csv(csvdata,csvfileoutput, row.names=FALSE,quote = FALSE)
  
  
  presentation_day_gbm_accu <- mean(info$label == label_pred_baseline)
  cat("The accuracy of GBM baseline model is", presentation_day_gbm_accu*100, "%.\n")
  
  presentation_day_advanced_accu <- mean(info$label == svm_rbf_pred)
  cat("The accuracy of Advanced model is", presentation_day_advanced_accu*100, "%.\n")
  
}

Reference

  • Du, S., Tao, Y., & Martinez, A. M. (2014). Compound facial expressions of emotion. Proceedings of the National Academy of Sciences, 111(15), E1454-E1462.
LS0tCnRpdGxlOiAiUHJvamVjdCAzOiBGYWNpYWwgRXhwcmVzc2lvbiBQcmVkaWN0aXZlIE1vZGVsaW5nIgphdXRob3I6ICJKaW5nYmluIENhbywgQ2h1YW5jaHVhbiBMaXUsIERlbm5pcyBTaHBpdHMsIFlpbmd5YW8gV3UsIFppa3VuIFpodWFuZyIKb3V0cHV0OgogIHBkZl9kb2N1bWVudDogZGVmYXVsdAogIGh0bWxfbm90ZWJvb2s6IGRlZmF1bHQKICBodG1sX2RvY3VtZW50OgogICAgZGZfcHJpbnQ6IHBhZ2VkCi0tLQoKYGBge3IgbWVzc2FnZT1GQUxTRX0KI1Rlc3QgQnJhbmNoIGNyZWF0ZWQKaWYoIXJlcXVpcmUoIlIubWF0bGFiIikpewogIGluc3RhbGwucGFja2FnZXMoIlIubWF0bGFiIikKfQppZighcmVxdWlyZSgicmVhZHhsIikpewogIGluc3RhbGwucGFja2FnZXMoInJlYWR4bCIpCn0KaWYoIXJlcXVpcmUoImRwbHlyIikpewogIGluc3RhbGwucGFja2FnZXMoImRwbHlyIikKfQppZighcmVxdWlyZSgicmVhZHhsIikpewogIGluc3RhbGwucGFja2FnZXMoInJlYWR4bCIpCn0KaWYoIXJlcXVpcmUoImdncGxvdDIiKSl7CiAgaW5zdGFsbC5wYWNrYWdlcygiZ2dwbG90MiIpCn0KaWYoIXJlcXVpcmUoImNhcmV0IikpewogIGluc3RhbGwucGFja2FnZXMoImNhcmV0IikKfQppZighcmVxdWlyZSgiZ2xtbmV0IikpewogIGluc3RhbGwucGFja2FnZXMoImdsbW5ldCIpCn0KaWYoIXJlcXVpcmUoIldlaWdodGVkUk9DIikpewogIGluc3RhbGwucGFja2FnZXMoIldlaWdodGVkUk9DIikKfQppZighcmVxdWlyZSgiZ2JtIikpewogIGluc3RhbGwucGFja2FnZXMoImdibSIpCn0KaWYoIXJlcXVpcmUoIkRNd1IiKSl7CiAgaW5zdGFsbC5wYWNrYWdlcygiRE13UiIpCn0KaWYoIXJlcXVpcmUoIk9wZW5JbWFnZVIiKSl7CiBpbnN0YWxsLnBhY2thZ2VzKCJPcGVuSW1hZ2VSIikKfQppZighcmVxdWlyZSgiQVVDIikpewogaW5zdGFsbC5wYWNrYWdlcygiQVVDIikKfQppZighcmVxdWlyZSgiZTEwNzEiKSl7CiBpbnN0YWxsLnBhY2thZ2VzKCJlMTA3MSIpCn0KaWYoIXJlcXVpcmUoInJhbmRvbUZvcmVzdCIpKXsKIGluc3RhbGwucGFja2FnZXMoInJhbmRvbUZvcmVzdCIpCn0KaWYoIXJlcXVpcmUoInhnYm9vc3QiKSl7CiBpbnN0YWxsLnBhY2thZ2VzKCJ4Z2Jvb3N0IikKfQppZighcmVxdWlyZSgidGliYmxlIikpewogaW5zdGFsbC5wYWNrYWdlcygidGliYmxlIikKfQppZighcmVxdWlyZSgiUk9TRSIpKXsKIGluc3RhbGwucGFja2FnZXMoIlJPU0UiKQp9CmlmKCFyZXF1aXJlKCJ0aWR5dmVyc2UiKSl7CiBpbnN0YWxsLnBhY2thZ2VzKCJ0aWR5dmVyc2UiKQp9CmlmKCFyZXF1aXJlKCJjYVRvb2xzIikpewogIGluc3RhbGwucGFja2FnZXMoImNhVG9vbHMiKQp9CmlmKCFyZXF1aXJlKCJwcmVkaWN0aW9uIikpewogIGluc3RhbGwucGFja2FnZXMoInByZWRpY3Rpb24iKQp9CmlmKCFyZXF1aXJlKCJwUk9DIikpewogIGluc3RhbGwucGFja2FnZXMoInBST0MiKQp9CgpsaWJyYXJ5KFIubWF0bGFiKQpsaWJyYXJ5KHJlYWR4bCkKbGlicmFyeShkcGx5cikKbGlicmFyeShnZ3Bsb3QyKQpsaWJyYXJ5KGNhcmV0KQpsaWJyYXJ5KGdsbW5ldCkKbGlicmFyeShXZWlnaHRlZFJPQykKbGlicmFyeShnYm0pCmxpYnJhcnkoRE13UikKIyMjIG5ldyBsaWJyYXJpZXMKbGlicmFyeShPcGVuSW1hZ2VSKQpsaWJyYXJ5KEFVQykKbGlicmFyeShlMTA3MSkKbGlicmFyeShyYW5kb21Gb3Jlc3QpCmxpYnJhcnkoeGdib29zdCkKbGlicmFyeSh0aWJibGUpCmxpYnJhcnkoUk9TRSkKbGlicmFyeSh0aWR5dmVyc2UpCmxpYnJhcnkoY2FUb29scykKbGlicmFyeShwcmVkaWN0aW9uKQpsaWJyYXJ5KHBST0MpCmBgYAoKCiMjIyBTdGVwIDAgc2V0IHdvcmsgZGlyZWN0b3JpZXMKYGBge3Igd2tkaXIsIGV2YWw9RkFMU0V9CnNldC5zZWVkKDIwMjApCmBgYAoKUHJvdmlkZSBkaXJlY3RvcmllcyBmb3IgdHJhaW5pbmcgaW1hZ2VzLiBUcmFpbmluZyBpbWFnZXMgYW5kIFRyYWluaW5nIGZpZHVjaWFsIHBvaW50cyB3aWxsIGJlIGluIGRpZmZlcmVudCBzdWJmb2xkZXJzLiAKCmBgYHtyfQp0cmFpbl9kaXIgPC0gIi4uL2RhdGEvdHJhaW5fc2V0LyIgIyBUaGlzIHdpbGwgYmUgbW9kaWZpZWQgZm9yIGRpZmZlcmVudCBkYXRhIHNldHMuCnRyYWluX2ltYWdlX2RpciA8LSBwYXN0ZSh0cmFpbl9kaXIsICJpbWFnZXMvIiwgc2VwPSIiKQp0cmFpbl9wdF9kaXIgPC0gcGFzdGUodHJhaW5fZGlyLCAgInBvaW50cy8iLCBzZXA9IiIpCnRyYWluX2xhYmVsX3BhdGggPC0gcGFzdGUodHJhaW5fZGlyLCAibGFiZWwuY3N2Iiwgc2VwPSIiKQpgYGAKCiMjIyBTdGVwIDE6IHNldCB1cCBjb250cm9scyBmb3IgZXZhbHVhdGlvbiBleHBlcmltZW50cy4KCkluIHRoaXMgY2h1bmssIHdlIGhhdmUgYSBzZXQgb2YgY29udHJvbHMgZm9yIHRoZSBldmFsdWF0aW9uIGV4cGVyaW1lbnRzLiAKCisgKFQvRikgY3Jvc3MtdmFsaWRhdGlvbiBvbiB0aGUgdHJhaW5pbmcgc2V0CisgKFQvRikgcmV3ZWlnaHRpbmcgdGhlIHNhbXBsZXMgZm9yIHRyYWluaW5nIHNldCAKKyAobnVtYmVyKSBLLCB0aGUgbnVtYmVyIG9mIENWIGZvbGRzCisgKFQvRikgcHJvY2VzcyBmZWF0dXJlcyBmb3IgdHJhaW5pbmcgc2V0CisgKFQvRikgcnVuIGV2YWx1YXRpb24gb24gYW4gaW5kZXBlbmRlbnQgdGVzdCBzZXQKKyAoVC9GKSBwcm9jZXNzIGZlYXR1cmVzIGZvciB0ZXN0IHNldAoKKyAoVC9GKSBydW4gaW1wcm92ZWQgZ2JtIG1vZGVsCisgKG51bWJlcikgZ2JtLm51bXRyZWVzLCB0aGUgbnVtYmVyIG9mIHRyZWVzIHRvIHVzZSBpbiBHQk0gYmFzZWxpbmUKKyAoVC9GKSByZXR1cm4gcG9seW5vbWlhbCBmZWF0dXJlcyBtYXRyaXggb25seQorIChUL0YpIGFkZCBwb2x5bm9taWFsIGZlYXR1cmVzIHRvIHN0YXJ0ZXIgY29kZSBmZWF0dXJlcyBtYXRyaXgKCisgKFQvRikgcnVuIHN2bSBtb2RlbAorIChUL0YpIHBlcmZvcm0gbW9kZWwgc2VsZWN0aW9uIG92ZXIgYSBsaXN0IG9mIHN2bSBtb2RlbHMKKyAoVC9GKSBydW4gZXZhbHVhdGlvbiBvbiB0aGUgdGVzdCBzZXQKCisgKFQvRikgcnVuIHJhbmRvbSBmb3Jlc3QgbW9kZWwKKyAoVC9GKSByZWJhbGFuY2UgdHJhaW5pbmcgc2V0CisgKFQvRikgdHJhaW4gcmFuZG9tIGZvcmVzdCBtb2RlbAorIChUL0YpIHJ1biBldmFsdWF0aW9uIG9uIHRoZSB0ZXN0IHNldAorIChUL0YpIHR1bmUgaHlwZXJwYXJhbWV0ZXJzIGZvciByYW5kb20gZm9yZXN0IG1vZGVsCgorIChUL0YpIHJ1biByaWRnZSBtb2RlbAorICgwLzEpIGFscGhhLCBhbHBoYT0wIGZvciByaWRnZSByZWdyZXNzaW9uLCBhbHBoYT0xIGZvciBsYXNzbyByZWdyZXNzaW9uCisgKFQvRikgdHJhaW4gcmlkZ2UgbW9kZWwKCisgKFQvRikgcnVuIFBDQStMREEgbW9kZWwKKyAoVC9GKSBydW4gZGlmZmVyZW50IHByaW5jaXBhbCBjb21wb25lbnRzCisgKFQvRikgcnVuIExEQSBvbiB0cmFpbmluZyBzZXQKKyAoVC9GKSBydW4gZXZhbHVhdGlvbiBvbiB0aGUgdGVzdCBzZXQKCmBgYHtyIGV4cF9zZXR1cH0KcnVuLnByZXNlbnRhdGlvbi5kYXkgPC0gVFJVRSAjcHJlc2VudGF0aW9uIGRheSBmbGFnLiBObyB0cmFpbmluZy4gR2VuZXJhdGUgYSBjc3YgZmlsZQpydW4uY3YgPC0gVFJVRSAjIHJ1biBjcm9zcy12YWxpZGF0aW9uIG9uIHRoZSB0cmFpbmluZyBzZXQKc2FtcGxlLnJld2VpZ2h0IDwtIFRSVUUgIyBydW4gc2FtcGxlIHJld2VpZ2h0aW5nIGluIG1vZGVsIHRyYWluaW5nCksgPC0gNSAgIyBudW1iZXIgb2YgQ1YgZm9sZHMKcnVuLmZlYXR1cmUudHJhaW4gPC0gVFJVRSAjIHByb2Nlc3MgZmVhdHVyZXMgZm9yIHRyYWluaW5nIHNldApydW4udGVzdCA8LSBUUlVFICMgcnVuIGV2YWx1YXRpb24gb24gYW4gaW5kZXBlbmRlbnQgdGVzdCBzZXQKcnVuLmZlYXR1cmUudGVzdCA8LSBUUlVFICMgcHJvY2VzcyBmZWF0dXJlcyBmb3IgdGVzdCBzZXQKCiMgZ2JtCnJ1bi5nYm0udHJhaW4gPC0gVFJVRSAjIGdibShpbXJvdmVkKSBpcyB0aGUgY2hvc2VuIGFkdmFuY2VkIG1vZGVsCnJ1bi5nYm0udGVzdCA8LSBUUlVFICMgZ2JtKGltcm92ZWQpIGlzIHRoZSBjaG9zZW4gYWR2YW5jZWQgbW9kZWwKZ2JtLm51bXRyZWVzIDwtIDEwMDAgI251bWJlciBvZiB0cmVlcyB0byB1c2UgaW4gZ2JtCgojZmVhdHVyZXMgb3B0aW9ucwpydW4ucG9seS5mZWF0dXJlIDwtIFRSVUUgIyBwcm9jZXNzIHBvbHkgZmVhdHVyZXMKcnVuLmFkZC5wb2x5LmZlYXR1cmUgPC0gVFJVRSAjIGFuZCBwb2x5IGZlYXR1cmVzIHRvIGZlYXR1cmVzIG1hdHJpeAoKIyBzdm0KcnVuLnN2bSA8LSBGQUxTRSAjIHN2bSBpcyB0aGUgY2hvc2VuIGFkdmFuY2VkIG1vZGVsCm1vZGVsLnNlbGVjdGlvbiA8LSBUUlVFICMgcGVyZm9ybSBtb2RlbCBzZWxlY3Rpb24gb24gc3ZtIG1vZGVscwpydW4uc3ZtLnRlc3QgPC0gVFJVRSAjIGV2YWx1YXRlIHBlcmZvcm1hbmNlIG9uIHRoZSB0ZXN0IHNldAoKIyByYW5kb20gZm9yZXN0CnJ1bi5yZiA8LSBGQUxTRSAjIHJhbmRvbSBmb3Jlc3QgaXMgdGhlIGNob3NlbiBhZHZhbmNlZCBtb2RlbApydW4uYmFsYW5jZWQuZGF0YSA8LSBUUlVFICMgd2hldGhlciBvciBub3QgYmFsYW5jZSB0aGUgZGF0YQp0cmFpbi5yYW5kb20uZm9yZXN0IDwtIEZBTFNFICMgdHJhaW4gcmFuZG9tIGZvcmVzdCBtb2RlbAp0ZXN0LnJhbmRvbS5mb3Jlc3QgPC0gVFJVRSAjIHRlc3QgcmFuZG9tIGZvcmVzdCBtb2RlbAp0dW5lLnJhbmRvbS5mb3Jlc3QgPC0gRkFMU0UgIyB0dW5lIHJhbmRvbSBmb3Jlc3QgbW9kZWwKCiMgcmlkZ2UKcnVuLnJpZGdlIDwtIEZBTFNFICMgcmlkZ2UgaXMgdGhlIGNob3NlbiBhZHZhbmNlZCBtb2RlbAphbHBoYSA8LSAwICMgcmlkZ2UgcmVncmVzc2lvbgp0cmFpbi5yaWRnZSA8LSBUUlVFICMgdHJhaW4gcmlkZ2UgbW9kZWwKCiMgUENBICsgTERBCnJ1bi5wY2FfbGRhIDwtIEZBTFNFICMgUENBICsgTERBIGlzIHRoZSBjaG9zZW4gYWRjYW5jZWQgbW9kZWwKcnVuLnNlbGVjdF9QQyA8LSBUUlVFICNydW4gZGlmZmVyZW50IFBDcwpydW4ubGRhIDwtIFRSVUUgIyBydW4gbGRhIG9uIHRoZSB0cmFpbmluZyBzZXQKcnVuLnBjYV9sYWQudGVzdCA8LSBUUlVFICMgZXZhbHVhdGUgcGVyZm9ybWFuY2Ugb24gdGhlIHRlc3Qgc2V0CmBgYAoKVXNpbmcgY3Jvc3MtdmFsaWRhdGlvbiBvciBpbmRlcGVuZGVudCB0ZXN0IHNldCBldmFsdWF0aW9uLCB3ZSBjb21wYXJlIHRoZSBwZXJmb3JtYW5jZSBvZiBtb2RlbHMgd2l0aCBkaWZmZXJlbnQgc3BlY2lmaWNhdGlvbnMuIAoKIyMjIFN0ZXAgMjogaW1wb3J0IGRhdGEgYW5kIHRyYWluLXRlc3Qgc3BsaXQgCmBgYHtyfQojdHJhaW4tdGVzdCBzcGxpdAppbmZvIDwtIHJlYWQuY3N2KHRyYWluX2xhYmVsX3BhdGgpCm4gPC0gbnJvdyhpbmZvKSAjZ2V0IG51bWJlciBvZiByb3dzIGZyb20gY3N2Cm5fdHJhaW4gPC0gcm91bmQobiooNC81KSwgMCkgI3VzZSA0LzUgYW1vdW50IG9mIGRhdGEgZm9yIHRyYWluaW5nCnRyYWluX2lkeCA8LSBzYW1wbGUoaW5mbyRJbmRleCwgbl90cmFpbiwgcmVwbGFjZSA9IEYpICNncmFiIGluZGV4ZXMgdXNlZCBmb3IgdHJhaW5pbmcKdGVzdF9pZHggPC0gc2V0ZGlmZihpbmZvJEluZGV4LCB0cmFpbl9pZHgpICMgZ2V0IGluZGV4ZXMgbm90IHVzZWQgZm9yIHRyYWluaW5nCmBgYAoKRmlkdWNpYWwgcG9pbnRzIGFyZSBzdG9yZWQgaW4gbWF0bGFiIGZvcm1hdC4gSW4gdGhpcyBzdGVwLCB3ZSByZWFkIHRoZW0gYW5kIHN0b3JlIHRoZW0gaW4gYSBsaXN0LgoKYGBge3IgcmVhZCBmaWR1Y2lhbCBwb2ludHN9CiNmdW5jdGlvbiB0byByZWFkIGZpZHVjaWFsIHBvaW50cwojaW5wdXQ6IGluZGV4CiNvdXRwdXQ6IG1hdHJpeCBvZiBmaWR1Y2lhbCBwb2ludHMgY29ycmVzcG9uZGluZyB0byB0aGUgaW5kZXgKbl9maWxlcyA8LSBsZW5ndGgobGlzdC5maWxlcyh0cmFpbl9pbWFnZV9kaXIsJypqcGcnKSkKcmVhZE1hdC5tYXRyaXggPC0gZnVuY3Rpb24oaW5kZXgpewogICAgIHJldHVybihyb3VuZChyZWFkTWF0KHBhc3RlMCh0cmFpbl9wdF9kaXIsIHNwcmludGYoIiUwNGQiLCBpbmRleCksICIubWF0IikpW1sxXV0sMCkpCn0KCmlmIChydW4ucHJlc2VudGF0aW9uLmRheSl7CiAgdGVzdF9pZHggPC0gYygxOm5fZmlsZXMpICNzYW1wbGUobl9maWxlcywgbl9maWxlcywgcmVwbGFjZSA9IEYpCiAgcnVuLmdibS50cmFpbiA8LSBGQUxTRQogIHJ1bi5mZWF0dXJlLnRyYWluIDwtIEZBTFNFCiAgcnVuLmdibS50ZXN0IDwtIFRSVUUKICBydW4uc3ZtIDwtIFRSVUUKICBtb2RlbC5zZWxlY3Rpb24gPC0gRkFMU0UKICBydW4uc3ZtLnRlc3QgPC0gVFJVRQp9CgojbG9hZCBmaWR1Y2lhbCBwb2ludHMKZmlkdWNpYWxfcHRfbGlzdCA8LSBsYXBwbHkoMTpuX2ZpbGVzLCByZWFkTWF0Lm1hdHJpeCkKc2F2ZShmaWR1Y2lhbF9wdF9saXN0LCBmaWxlPSIuLi9vdXRwdXQvZmlkdWNpYWxfcHRfbGlzdC5SRGF0YSIpCmBgYAoKIyMjIFN0ZXAgMzogY29uc3RydWN0IGZlYXR1cmVzIGFuZCByZXNwb25zZXMKCisgVGhlIGZvbGxvdyBwbG90cyBzaG93IGhvdyBwYWlyd2lzZSBkaXN0YW5jZSBiZXR3ZWVuIGZpZHVjaWFsIHBvaW50cyBjYW4gd29yayBhcyBmZWF0dXJlIGZvciBmYWNpYWwgZW1vdGlvbiByZWNvZ25pdGlvbi4KCiAgKyBJbiB0aGUgZmlyc3QgY29sdW1uLCA3OCBmaWR1Y2lhbHMgcG9pbnRzIG9mIGVhY2ggZW1vdGlvbiBhcmUgbWFya2VkIGluIG9yZGVyLiAKICArIEluIHRoZSBzZWNvbmQgY29sdW1uIGRpc3RyaWJ1dGlvbnMgb2YgdmVydGljYWwgZGlzdGFuY2UgYmV0d2VlbiByaWdodCBwdXBpbCgxKSBhbmQgIHJpZ2h0IGJyb3cgcGVhaygyMSkgYXJlIHNob3duIGluICBoaXN0b2dyYW1zLiBGb3IgZXhhbXBsZSwgdGhlIGRpc3RhbmNlIG9mIGFuIGFuZ3J5IGZhY2UgdGVuZHMgdG8gYmUgc2hvcnRlciB0aGFuIHRoYXQgb2YgYSBzdXJwcmlzZWQgZmFjZS4KICArIFRoZSB0aGlyZCBjb2x1bW4gaXMgdGhlIGRpc3RyaWJ1dGlvbnMgb2YgdmVydGljYWwgZGlzdGFuY2VzIGJldHdlZW4gcmlnaHQgbW91dGggY29ybmVyKDUwKQphbmQgdGhlIG1pZHBvaW50IG9mIHRoZSB1cHBlciBsaXAoNTIpLiAgRm9yIGV4YW1wbGUsIHRoZSBkaXN0YW5jZSBvZiBhbiBoYXBweSBmYWNlIHRlbmRzIHRvIGJlIHNob3J0ZXIgdGhhbiB0aGF0IG9mIGEgc2FkIGZhY2UuCgohW0ZpZ3VyZTFdKC4uL2ZpZ3MvZmVhdHVyZV92aXN1YWxpemF0aW9uLmpwZykKCmBmZWF0dXJlLlJgIGlzIHRoZSB3cmFwcGVyIGZvciBhbGwgZmVhdHVyZSBlbmdpbmVlcmluZyBmdW5jdGlvbnMgYW5kIG9wdGlvbnMuIFRoZSBmdW5jdGlvbiBgZmVhdHVyZSggKWAgaGF2ZSBvcHRpb25zIHRoYXQgY29ycmVzcG9uZCB0byBkaWZmZXJlbnQgc2NlbmFyaW9zIGZvciB0aGUgcHJvamVjdCBhbmQgcHJvZHVjZXMgYW4gUiBvYmplY3QgdGhhdCBjb250YWlucyBmZWF0dXJlcyBhbmQgcmVzcG9uc2VzIHRoYXQgYXJlIHJlcXVpcmVkIGJ5IGFsbCB0aGUgbW9kZWxzIHRoYXQgYXJlIGdvaW5nIHRvIGJlIGV2YWx1YXRlZCBsYXRlci4gCiAgCiAgKyBgZmVhdHVyZS5SYAogICsgSW5wdXQ6IGxpc3Qgb2YgaW1hZ2VzIG9yIGZpZHVjaWFsIHBvaW50CiAgKyBPdXRwdXQ6IGFuIFJEYXRhIGZpbGUgdGhhdCBjb250YWlucyBleHRyYWN0ZWQgZmVhdHVyZXMgYW5kIGNvcnJlc3BvbmRpbmcgcmVzcG9uc2VzCgpgYGB7ciBmZWF0dXJlfQpzb3VyY2UoIi4uL2xpYi9mZWF0dXJlLlIiKQp0bV9mZWF0dXJlX3RyYWluIDwtIE5BCmdibV90bV9mZWF0dXJlX3RyYWluIDwtIE5BCmlmKHJ1bi5mZWF0dXJlLnRyYWluKXsKICB0bV9mZWF0dXJlX3RyYWluIDwtIHN5c3RlbS50aW1lKGRhdF90cmFpbjwtZmVhdHVyZShmaWR1Y2lhbF9wdF9saXN0LHRyYWluX2lkeCwgcnVuLnBvbHkuZmVhdHVyZSwgcnVuLmFkZC5wb2x5LmZlYXR1cmUpKQogIGdibV90bV9mZWF0dXJlX3RyYWluIDwtIHN5c3RlbS50aW1lKGdibV9kYXRfdHJhaW48LWZlYXR1cmUoZmlkdWNpYWxfcHRfbGlzdCx0cmFpbl9pZHgsIEZBTFNFLCBGQUxTRSkpCiAgc2F2ZShnYm1fZGF0X3RyYWluLCBmaWxlPSIuLi9vdXRwdXQvZ2JtX2ZlYXR1cmVfdHJhaW4uUkRhdGEiKQogIHNhdmUoZGF0X3RyYWluLCBmaWxlPSIuLi9vdXRwdXQvZmVhdHVyZV90cmFpbi5SRGF0YSIpCn1lbHNlewogICNsb2FkKGZpbGU9Ii4uL291dHB1dC9mZWF0dXJlX3RyYWluLlJEYXRhIikKICAjbG9hZChmaWxlPSIuLi9vdXRwdXQvZ2JtX2ZlYXR1cmVfdHJhaW4uUkRhdGEiKQp9Cgp0bV9mZWF0dXJlX3Rlc3QgPC0gTkEKZ2JtX3RtX2ZlYXR1cmVfdGVzdCA8LSBOQQppZihydW4uZmVhdHVyZS50ZXN0KXsKICB0bV9mZWF0dXJlX3Rlc3QgPC0gc3lzdGVtLnRpbWUoZGF0X3Rlc3QgPC0gZmVhdHVyZShmaWR1Y2lhbF9wdF9saXN0LCB0ZXN0X2lkeCwgcnVuLnBvbHkuZmVhdHVyZSwgcnVuLmFkZC5wb2x5LmZlYXR1cmUpKQogIGdibV90bV9mZWF0dXJlX3Rlc3QgPC0gc3lzdGVtLnRpbWUoZ2JtX2RhdF90ZXN0IDwtIGZlYXR1cmUoZmlkdWNpYWxfcHRfbGlzdCwgdGVzdF9pZHgsIEZBTFNFLCBGQUxTRSkpCiAgc2F2ZShnYm1fZGF0X3Rlc3QsIGZpbGU9Ii4uL291dHB1dC9nYm1fZmVhdHVyZV90ZXN0LlJEYXRhIikKICBzYXZlKGRhdF90ZXN0LCBmaWxlPSIuLi9vdXRwdXQvZmVhdHVyZV90ZXN0LlJEYXRhIikKfWVsc2V7CiAgbG9hZChmaWxlPSIuLi9vdXRwdXQvZmVhdHVyZV90ZXN0LlJEYXRhIikKICBsb2FkKGZpbGU9Ii4uL291dHB1dC9nYm1fZmVhdHVyZV90ZXN0LlJEYXRhIikKfQpgYGAKCiMjIyBTdGVwIDQ6IHRyYWluIGNsYXNzaWZpY2F0aW9uIG1vZGVscyB3aXRoIHRyYWluaW5nIGZlYXR1cmVzIGFuZCByZXNwb25zZXM7IHJ1biB0ZXN0IG9uIHRlc3QgaW1hZ2VzCgpDYWxsIHRoZSB0cmFpbiBtb2RlbCBhbmQgdGVzdCBtb2RlbCBmcm9tIGxpYnJhcnkuIAoKYHRyYWluLlJgIGFuZCBgdGVzdC5SYCBhcmUgd3JhcHBlcnMgZm9yIGFsbCBtb2RlbCB0cmFpbmluZyBzdGVwcyBhbmQgY2xhc3NpZmljYXRpb24vcHJlZGljdGlvbiBzdGVwcy4gCgorIGB0cmFpbi5SYAogICsgSW5wdXQ6IGEgZGF0YSBmcmFtZSBjb250YWluaW5nIGZlYXR1cmVzIGFuZCBsYWJlbHMgYW5kIGEgcGFyYW1ldGVyIGxpc3QuCiAgKyBPdXRwdXQ6YSB0cmFpbmVkIG1vZGVsCisgYHRlc3QuUmAKICArIElucHV0OiB0aGUgZml0dGVkIGNsYXNzaWZpY2F0aW9uIG1vZGVsIHVzaW5nIHRyYWluaW5nIGRhdGEgYW5kIHByb2Nlc3NlZCBmZWF0dXJlcyBmcm9tIHRlc3RpbmcgaW1hZ2VzIAogICsgSW5wdXQ6IGFuIFIgb2JqZWN0IHRoYXQgY29udGFpbnMgYSB0cmFpbmVkIGNsYXNzaWZpZXIuCiAgKyBPdXRwdXQ6IHRyYWluaW5nIG1vZGVsIHNwZWNpZmljYXRpb24KCisgSW4gdGhpcyBTdGFydGVyIENvZGUsIHdlIHVzZSBsb2dpc3RpYyByZWdyZXNzaW9uIHdpdGggTEFTU08gcGVuYWx0eSB0byBkbyBjbGFzc2lmaWNhdGlvbi4gCgpgYGB7ciBsb2FkbGlifQpzb3VyY2UoIi4uL2xpYi90cmFpbi5SIikgCnNvdXJjZSgiLi4vbGliL3Rlc3QuUiIpCmBgYAoKIyBCYXNlbGluZSBNb2RlbAoKCi0tLS0tLS0tVEhJUyBJUyBUTyBTRVBBUkFURSBFQUNIIE1PREVMLiBUSElTIElTIFRPIFNFUEFSQVRFIEVBQ0ggTU9ERUwuIFRISVMgSVMgVE8gU0VQQVJBVEUgRUFDSCBNT0RFTC4tLS0tLS0tLS0tCgoKCgoKIyBBZHZhbmNlZCBNb2RlbCAxOiBJbXByb3ZlZCBHQk0gTW9kZWwKCiogTW9kZWwgVHJhaW5pbmcKCmBgYHtyfQppZiAocnVuLmdibS50cmFpbil7CiAgaWYgKHNhbXBsZS5yZXdlaWdodCl7CiAgICAKICAgIGdibV9kYXRfdHJhaW4kbGFiZWwgPC0gYXMuZmFjdG9yKGdibV9kYXRfdHJhaW4kbGFiZWwpCiAgICBkYXRfdHJhaW5fYmFsYW5jZWRfZ2JtIDwtIFNNT1RFKGxhYmVsIH4gLiwgZ2JtX2RhdF90cmFpbiwgcGVyYy5vdmVyID0gMTAwLCBwZXJjLnVuZGVyPTIwMCkKICAgICNzYXZlKGRhdF90cmFpbl9iYWxhbmNlZF9yb3NlLCBmaWxlPSIuLi9vdXRwdXQvYmFsYW5jZWRfZGF0YS5SRGF0YSIpCiAgICB0YWJsZShkYXRfdHJhaW5fYmFsYW5jZWRfZ2JtJGxhYmVsKQogICAgCiAgICBnYm1fdG1fdHJhaW4gPC0gc3lzdGVtLnRpbWUoZ2JtX3RyYWluIDwtIHRyYWluX2dibShkYXRfdHJhaW5fYmFsYW5jZWRfZ2JtLCBzPTAuMSwgSz1LLCBuPWdibS5udW10cmVlcyx3ID0gTlVMTCkpCiAgICAKICB9IGVsc2UgewogICAgZ2JtX3RtX3RyYWluIDwtIHN5c3RlbS50aW1lKGdibV90cmFpbiA8LSB0cmFpbl9nYm0oZ2JtX2RhdF90cmFpbiwgcz0wLjEsIEs9Sywgbj1nYm0ubnVtdHJlZXMsdyA9IE5VTEwpKQogIH0KICAKICAjIHBsb3QgdGhlIHBlcmZvcm1hbmNlCiAgYmVzdC5pdGVyLm9vYiA8LSBnYm0ucGVyZihnYm1fdHJhaW4sbWV0aG9kPSJPT0IiKSAgIyByZXR1cm5zIG91dC1vZi1iYWcgZXN0aW1hdGVkIGJlc3QgbnVtYmVyIG9mIHRyZWVzCiAgcHJpbnQoYmVzdC5pdGVyLm9vYikKICBiZXN0Lml0ZXIuY3YgPC0gZ2JtLnBlcmYoZ2JtX3RyYWluLG1ldGhvZD0iY3YiKSAgICMgcmV0dXJucyBLLWZvbGQgY3YgZXN0aW1hdGUgb2YgYmVzdCBudW1iZXIgb2YgdHJlZXMKICBwcmludChiZXN0Lml0ZXIuY3YpCiAgCiAgc2F2ZVJEUyhnYm1fdHJhaW4sICIuLi9vdXRwdXQvZ2JtX21vZGVsLnJkcyIpCiAgc2F2ZShnYm1fdG1fdHJhaW4sIGJlc3QuaXRlci5jdiwgZmlsZT0iLi4vb3V0cHV0L2dibV9vdXRwdXRzLlJEYXRhIikKfQoKYGBgCgoqIEV2YWx1YXRpb24gb24gVGVzdCBTZXQKCmBgYHtyfQppZihydW4uZ2JtLnRlc3QpewogIGxvYWQoZmlsZT0iLi4vb3V0cHV0L2dibV9vdXRwdXRzLlJEYXRhIikKICBnYm1fdG1fdGVzdCA9IE5BCiAgZmVhdHVyZV90ZXN0IDwtIGFzLm1hdHJpeChnYm1fZGF0X3Rlc3RbLCAxOm5jb2woZ2JtX2RhdF90ZXN0KS0xXSkKICAKICBnYm1fdHJhaW4gPC0gcmVhZFJEUygiLi4vb3V0cHV0L2dibV9tb2RlbC5yZHMiKQogIGdibV90bV90ZXN0IDwtIHN5c3RlbS50aW1lKHByb2JfcHJlZF9iYXNlbGluZTwtdGVzdF9nYm0oZ2JtX3RyYWluLGFzLmRhdGEuZnJhbWUoZmVhdHVyZV90ZXN0KSxuPWJlc3QuaXRlci5jdixwcmVkLnR5cGUgPSAncmVzcG9uc2UnKSkKICAKICBsYWJlbF9wcmVkX2Jhc2VsaW5lIDwtIGNvbG5hbWVzKHByb2JfcHJlZF9iYXNlbGluZSlbYXBwbHkocHJvYl9wcmVkX2Jhc2VsaW5lLCAxLCB3aGljaC5tYXgpXQp9CmBgYAoKc2hvdyBnYm0gYWNjdXJhY3kgYW5kIEFVQwpgYGB7cn0KaWYgKHJ1bi5nYm0udGVzdCl7CiAgbG9hZChmaWxlPSIuLi9vdXRwdXQvZ2JtX291dHB1dHMuUkRhdGEiKQogIGdibV9hY2N1IDwtIG1lYW4oZ2JtX2RhdF90ZXN0JGxhYmVsID09IGxhYmVsX3ByZWRfYmFzZWxpbmUpCiAgZ2JtLmF1YyA8LSBXZWlnaHRlZFJPQyhhcy5udW1lcmljKGxhYmVsX3ByZWRfYmFzZWxpbmUpLCBnYm1fZGF0X3Rlc3QkbGFiZWwpCiAgZ2JtX2F1YyA9IFdlaWdodGVkQVVDKGdibS5hdWMpCiAgY2F0KCJUaW1lIGZvciBjb25zdHJ1Y3RpbmcgZ2JtIHRyYWluaW5nIGZlYXR1cmVzPSIsIGdibV90bV9mZWF0dXJlX3RyYWluWzFdLCAicyBcbiIpCiAgY2F0KCJUaW1lIGZvciBjb25zdHJ1Y3RpbmcgZ2JtIHRlc3RpbmcgZmVhdHVyZXM9IiwgZ2JtX3RtX2ZlYXR1cmVfdGVzdFsxXSwgInMgXG4iKQogIGNhdCgiVGhlIEFVQyBvZiBnYm0gbW9kZWwgaXMiLCBnYm1fYXVjLCAiLlxuIikKICBjYXQoIlRoZSBhY2N1cmFjeSBvZiBHQk0gYmFzZWxpbmUgbW9kZWwgaXMiLCBnYm1fYWNjdSoxMDAsICIlLlxuIikKICBjYXQoIlRpbWUgZm9yIHRyYWluaW5nIGdibSBtb2RlbD0iLCBnYm1fdG1fdHJhaW5bMV0sICJzIFxuIikgCiAgY2F0KCJUaW1lIGZvciB0ZXN0aW5nIG1vZGVsPSIsIGdibV90bV90ZXN0WzFdLCAicyBcbiIpCn0KYGBgCi0tLS0tLS0tVEhJUyBJUyBUTyBTRVBBUkFURSBFQUNIIE1PREVMLiBUSElTIElTIFRPIFNFUEFSQVRFIEVBQ0ggTU9ERUwuIFRISVMgSVMgVE8gU0VQQVJBVEUgRUFDSCBNT0RFTC4tLS0tLS0tLS0tCgoKIyBBZHZhbmNlZCBNb2RlbCAyOiBSYW5kb20gRm9yZXN0CgoqIEJhbGFuY2UgVHJhaW4gU2V0CgpgYGB7cn0KaWYocnVuLnJmKXsKICAjIHRyYW5zZmVyIGxhYmVsIGNvbHVtbiBmcm9tIGZhY3RvciB0byBudW1lcmljCiAgZGF0X3RyYWluJGxhYmVsIDwtIGFzLm51bWVyaWMoZGF0X3RyYWluJGxhYmVsKQogIGRhdF90ZXN0JGxhYmVsIDwtIGFzLm51bWVyaWMoZGF0X3Rlc3QkbGFiZWwpCgogIGlmKHJ1bi5iYWxhbmNlZC5kYXRhKXsKICAgIGRhdF90cmFpbl9iYWxhbmNlZF9yb3NlPC1ST1NFKGxhYmVsfi4sIGRhdF90cmFpbixzZWVkPTIwMjApJGRhdGEKICAgIHNhdmUoZGF0X3RyYWluX2JhbGFuY2VkX3Jvc2UsIGZpbGU9Ii4uL291dHB1dC9iYWxhbmNlZF90cmFpbl9kYXRhX3Jvc2UuUkRhdGEiKQogICAgZGF0X3Rlc3RfYmFsYW5jZWRfcm9zZSA8LSBST1NFKGxhYmVsfi4sIGRhdF90ZXN0LCBzZWVkPTIwMjApJGRhdGEKICAgIHNhdmUoZGF0X3Rlc3RfYmFsYW5jZWRfcm9zZSwgZmlsZSA9ICIuLi9vdXRwdXQvYmFsYW5jZWRfdGVzdF9kYXRhX3Jvc2UuUkRhdGEiKQogIH0gZWxzZSB7CiAgICBsb2FkKGZpbGUgPSAiLi4vb3V0cHV0L2JhbGFuY2VkX3RyYWluX2RhdGFfcm9zZS5SRGF0YSIpCiAgICBsb2FkKGZpbGUgPSAiLi4vb3V0cHV0L2JhbGFuY2VkX3Rlc3RfZGF0YV9yb3NlLlJEYXRhIikKICB9CiAgdGFibGUoZGF0X3RyYWluX2JhbGFuY2VkX3Jvc2UkbGFiZWwpCiAgdGFibGUoZGF0X3Rlc3RfYmFsYW5jZWRfcm9zZSRsYWJlbCkKfQpgYGAKCgoqIFR1bmUgUGFyYW1ldGVycyBmb3IgUmFuZG9tIEZvcmVzdAoKYGBge3J9CmlmKHJ1bi5yZil7CiAgc291cmNlKCIuLi9saWIvcmFuZG9tX2ZvcmVzdF9vbGRfZmVhdHVyZS5SIikKICBpZih0dW5lLnJhbmRvbS5mb3Jlc3QpewogICAgdGltZS5yZi50dW5lIDwtIHN5c3RlbS50aW1lKHJmLnR1bmUgPC0gcmFuZG9tX2ZvcmVzdF90dW5lKGRhdF90cmFpbl9iYWxhbmNlZF9yb3NlKSkKICAgIHNhdmUocmYudHVuZSwgZmlsZT0iLi4vb3V0cHV0L3JmX3R1bmUuUkRhdGEiKQogICAgc2F2ZSh0aW1lLnJmLnR1bmUsIGZpbGUgPSAiLi4vb3V0cHV0L3JmX3R1bmVfdGltZS5SRGF0YSIpCiAgfWVsc2V7CiAgICBsb2FkKCIuLi9vdXRwdXQvcmZfdHVuZS5SRGF0YSIpCiAgICBsb2FkKCIuLi9vdXRwdXQvcmZfdHVuZV90aW1lLlJEYXRhIikKICB9CiAgcmYudHVuZQogIHRpbWUucmYudHVuZVsxXQp9CmBgYAoKbXRyeSA9IDMwOCBpcyB0aGUgYmVzdC4KCiogVHVuZSBUcmVlcyBhbmQgTm9kZXMKCmBgYHtyfQppZihydW4ucmYpewogIHNvdXJjZSgiLi4vbGliL3JhbmRvbV9mb3Jlc3Rfb2xkX2ZlYXR1cmUuUiIpCiAgaWYodHVuZS5yYW5kb20uZm9yZXN0KXsKICAgICMgVHJhaW4gNTAwIHRyZWVzOgogICAgdGltZS5yZi50cmFpbi50cmVlNTAwIDwtIHN5c3RlbS50aW1lKHJhbmRvbV9mb3Jlc3RfZml0XzUwMF90cmVlcyA8LQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgcmFuZG9tX2ZvcmVzdF90cmFpbl81MDAoZGF0X3RyYWluX2JhbGFuY2VkX3Jvc2UsbXRyeSA9IDMwOCkpCiAgICBzYXZlKHJhbmRvbV9mb3Jlc3RfZml0XzUwMF90cmVlcywgZmlsZSA9ICIuLi9vdXRwdXQvcmZfdHJhaW5fNTAwX3RyZWVzLlJEYXRhIikKICAgIHNhdmUodGltZS5yZi50cmFpbi50cmVlNTAwLCBmaWxlID0gIi4uL291dHB1dC9yZl90cmFpbl81MDBfdHJlZXNfdGltZS5SRGF0YSIpCiAgICBjYXQoIjUwMCB0cmVlIHRpbWUiLCB0aW1lLnJmLnRyYWluLnRyZWU1MDApCiAgICAjIFRyYWluIDEwMDAgdHJlZXM6CiAgICB0aW1lLnJmLnRyYWluLnRyZWUxMDAwIDwtIHN5c3RlbS50aW1lKHJhbmRvbV9mb3Jlc3RfZml0XzEwMDBfdHJlZXMgPC0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICByYW5kb21fZm9yZXN0X3RyYWluXzEwMDAoZGF0X3RyYWluX2JhbGFuY2VkX3Jvc2UsbXRyeSA9IDMwOCkpCiAgICBzYXZlKHJhbmRvbV9mb3Jlc3RfZml0XzEwMDBfdHJlZXMsIGZpbGUgPSAiLi4vb3V0cHV0L3JmX3RyYWluXzEwMDBfdHJlZXMuUkRhdGEiKQogICAgc2F2ZSh0aW1lLnJmLnRyYWluLnRyZWUxMDAwLCBmaWxlID0gIi4uL291dHB1dC9yZl90cmFpbl8xMDAwX3RyZWVzX3RpbWUuUkRhdGEiKQogICAgY2F0KCIxMDAwIHRyZWUgdGltZSIsIHRpbWUucmYudHJhaW4udHJlZTEwMDApCiAgICAjIFRyYWluIDE1MDAgdHJlZXM6CiAgICB0aW1lLnJmLnRyYWluLnRyZWUxNTAwIDwtIHN5c3RlbS50aW1lKHJhbmRvbV9mb3Jlc3RfZml0XzE1MDBfdHJlZXMgPC0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICByYW5kb21fZm9yZXN0X3RyYWluXzE1MDAoZGF0X3RyYWluX2JhbGFuY2VkX3Jvc2UsbXRyeSA9IDMwOCkpCiAgICBzYXZlKHJhbmRvbV9mb3Jlc3RfZml0XzE1MDBfdHJlZXMsIGZpbGUgPSAiLi4vb3V0cHV0L3JmX3RyYWluXzE1MDBfdHJlZXMuUkRhdGEiKQogICAgc2F2ZSh0aW1lLnJmLnRyYWluLnRyZWUxNTAwLCBmaWxlID0gIi4uL291dHB1dC9yZl90cmFpbl8xNTAwX3RyZWVzX3RpbWUuUkRhdGEiKQogICAgY2F0KCIxNTAwIHRyZWUgdGltZSIsIHRpbWUucmYudHJhaW4udHJlZTE1MDApCiAgICAjIFRyYWluIDIwMDAgdHJlZXM6CiAgICB0aW1lLnJmLnRyYWluLnRyZWUyMDAwIDwtIHN5c3RlbS50aW1lKHJhbmRvbV9mb3Jlc3RfZml0XzIwMDBfdHJlZXMgPC0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICByYW5kb21fZm9yZXN0X3RyYWluXzIwMDAoZGF0X3RyYWluX2JhbGFuY2VkX3Jvc2UsbXRyeSA9IDMwOCkpCiAgICBzYXZlKHJhbmRvbV9mb3Jlc3RfZml0XzIwMDBfdHJlZXMsIGZpbGUgPSAiLi4vb3V0cHV0L3JmX3RyYWluXzIwMDBfdHJlZXMuUkRhdGEiKQogICAgc2F2ZSh0aW1lLnJmLnRyYWluLnRyZWUyMDAwLCBmaWxlID0gIi4uL291dHB1dC9yZl90cmFpbl8yMDAwX3RyZWVzX3RpbWUuUkRhdGEiKQogICAgY2F0KCIyMDAwIHRyZWUgdGltZSIsIHRpbWUucmYudHJhaW4udHJlZTIwMDApCiAgICAjIFRyYWluIDI1MDAgdHJlZXM6CiAgICB0aW1lLnJmLnRyYWluLnRyZWUyNTAwIDwtIHN5c3RlbS50aW1lKHJhbmRvbV9mb3Jlc3RfZml0XzI1MDBfdHJlZXMgPC0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICByYW5kb21fZm9yZXN0X3RyYWluXzI1MDAoZGF0X3RyYWluX2JhbGFuY2VkX3Jvc2UsbXRyeSA9IDMwOCkpCiAgICBzYXZlKHJhbmRvbV9mb3Jlc3RfZml0XzI1MDBfdHJlZXMsIGZpbGUgPSAiLi4vb3V0cHV0L3JmX3RyYWluXzI1MDBfdHJlZXMuUkRhdGEiKQogICAgc2F2ZSh0aW1lLnJmLnRyYWluLnRyZWUyNTAwLCBmaWxlID0gIi4uL291dHB1dC9yZl90cmFpbl8yNTAwX3RyZWVzX3RpbWUuUkRhdGEiKQogICAgY2F0KCIyNTAwIHRyZWUgdGltZSIsIHRpbWUucmYudHJhaW4udHJlZTI1MDApCgogICAgIyBUcmFpbiAxMCBub2RlcwogICAgdGltZS5yZi50cmFpbi5ub2RlMTAgPC0gc3lzdGVtLnRpbWUocmFuZG9tX2ZvcmVzdF9maXRfMTBfbm9kZXMgPC0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgcmFuZG9tX2ZvcmVzdF90cmFpbl8xMChkYXRfdHJhaW5fYmFsYW5jZWRfcm9zZSxtdHJ5ID0gMzA4KSkKICAgIHNhdmUocmFuZG9tX2ZvcmVzdF9maXRfMTBfbm9kZXMsIGZpbGUgPSAiLi4vb3V0cHV0L3JmX3RyYWluXzEwX25vZGVzLlJEYXRhIikKICAgIHNhdmUodGltZS5yZi50cmFpbi5ub2RlMTAsIGZpbGUgPSAiLi4vb3V0cHV0L3JmX3RyYWluXzEwX25vZGVzX3RpbWUuUkRhdGEiKQogICAgY2F0KCIxMCBub2RlIHRpbWUiLCB0aW1lLnJmLnRyYWluLm5vZGUxMCkKICAgICMgVHJhaW4gMTUgbm9kZXMKICAgIHRpbWUucmYudHJhaW4ubm9kZTE1IDwtIHN5c3RlbS50aW1lKHJhbmRvbV9mb3Jlc3RfZml0XzE1X25vZGVzIDwtCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHJhbmRvbV9mb3Jlc3RfdHJhaW5fMTUoZGF0X3RyYWluX2JhbGFuY2VkX3Jvc2UsbXRyeSA9IDMwOCkpCiAgICBzYXZlKHJhbmRvbV9mb3Jlc3RfZml0XzE1X25vZGVzLCBmaWxlID0gIi4uL291dHB1dC9yZl90cmFpbl8xNV9ub2Rlcy5SRGF0YSIpCiAgICBzYXZlKHRpbWUucmYudHJhaW4ubm9kZTE1LCBmaWxlID0gIi4uL291dHB1dC9yZl90cmFpbl8xNV9ub2Rlc190aW1lLlJEYXRhIikKICAgIGNhdCgiMTUgbm9kZSB0aW1lIiwgdGltZS5yZi50cmFpbi5ub2RlMTUpCiAgICAjIFRyYWluIDIwIG5vZGVzCiAgICB0aW1lLnJmLnRyYWluLm5vZGUyMCA8LSBzeXN0ZW0udGltZShyYW5kb21fZm9yZXN0X2ZpdF8yMF9ub2RlcyA8LQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICByYW5kb21fZm9yZXN0X3RyYWluXzIwKGRhdF90cmFpbl9iYWxhbmNlZF9yb3NlLG10cnkgPSAzMDgpKQogICAgc2F2ZShyYW5kb21fZm9yZXN0X2ZpdF8yMF9ub2RlcywgZmlsZSA9ICIuLi9vdXRwdXQvcmZfdHJhaW5fMjBfbm9kZXMuUkRhdGEiKQogICAgc2F2ZSh0aW1lLnJmLnRyYWluLm5vZGUyMCwgZmlsZSA9ICIuLi9vdXRwdXQvcmZfdHJhaW5fMjBfbm9kZXNfdGltZS5SRGF0YSIpCiAgICBjYXQoIjIwIG5vZGUgdGltZSIsIHRpbWUucmYudHJhaW4ubm9kZTIwKQogICAgIyBUcmFpbiAyNSBub2RlcwogICAgdGltZS5yZi50cmFpbi5ub2RlMjUgPC0gc3lzdGVtLnRpbWUocmFuZG9tX2ZvcmVzdF9maXRfMjVfbm9kZXMgPC0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgcmFuZG9tX2ZvcmVzdF90cmFpbl8yNShkYXRfdHJhaW5fYmFsYW5jZWRfcm9zZSxtdHJ5ID0gMzA4KSkKICAgIHNhdmUocmFuZG9tX2ZvcmVzdF9maXRfMjVfbm9kZXMsIGZpbGUgPSAiLi4vb3V0cHV0L3JmX3RyYWluXzI1X25vZGVzLlJEYXRhIikKICAgIHNhdmUodGltZS5yZi50cmFpbi5ub2RlMjUsIGZpbGUgPSAiLi4vb3V0cHV0L3JmX3RyYWluXzI1X25vZGVzX3RpbWUuUkRhdGEiKQogICAgY2F0KCIyNSBub2RlIHRpbWUiLCB0aW1lLnJmLnRyYWluLm5vZGUyNSkKICAgICMgVHJhaW4gMzAgbm9kZXMKICAgIHRpbWUucmYudHJhaW4ubm9kZTMwIDwtIHN5c3RlbS50aW1lKHJhbmRvbV9mb3Jlc3RfZml0XzMwX25vZGVzIDwtCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHJhbmRvbV9mb3Jlc3RfdHJhaW5fMzAoZGF0X3RyYWluX2JhbGFuY2VkX3Jvc2UsbXRyeSA9IDMwOCkpCiAgICBzYXZlKHJhbmRvbV9mb3Jlc3RfZml0XzMwX25vZGVzLCBmaWxlID0gIi4uL291dHB1dC9yZl90cmFpbl8zMF9ub2Rlcy5SRGF0YSIpCiAgICBzYXZlKHRpbWUucmYudHJhaW4ubm9kZTMwLCBmaWxlID0gIi4uL291dHB1dC9yZl90cmFpbl8zMF9ub2Rlc190aW1lLlJEYXRhIikKICAgIGNhdCgiMzAgbm9kZSB0aW1lIiwgdGltZS5yZi50cmFpbi5ub2RlMzApCiAgfSBlbHNlIHsKICAgIGxvYWQoIi4uL291dHB1dC9yZl90cmFpbl81MDBfdHJlZXMuUkRhdGEiKQogICAgbG9hZCgiLi4vb3V0cHV0L3JmX3RyYWluXzEwMDBfdHJlZXMuUkRhdGEiKQogICAgbG9hZCgiLi4vb3V0cHV0L3JmX3RyYWluXzE1MDBfdHJlZXMuUkRhdGEiKQogICAgbG9hZCgiLi4vb3V0cHV0L3JmX3RyYWluXzIwMDBfdHJlZXMuUkRhdGEiKQogICAgbG9hZCgiLi4vb3V0cHV0L3JmX3RyYWluXzI1MDBfdHJlZXMuUkRhdGEiKQogICAgbG9hZCgiLi4vb3V0cHV0L3JmX3RyYWluXzEwX25vZGVzLlJEYXRhIikKICAgIGxvYWQoIi4uL291dHB1dC9yZl90cmFpbl8xNV9ub2Rlcy5SRGF0YSIpCiAgICBsb2FkKCIuLi9vdXRwdXQvcmZfdHJhaW5fMjBfbm9kZXMuUkRhdGEiKQogICAgbG9hZCgiLi4vb3V0cHV0L3JmX3RyYWluXzI1X25vZGVzLlJEYXRhIikKICAgIGxvYWQoIi4uL291dHB1dC9yZl90cmFpbl8zMF9ub2Rlcy5SRGF0YSIpCiAgfQoKCgogICNFcnJvciByYXRlIG9mIGVhY2ggaHlwZXJwYXJhbWV0ZXI6CgoKCiAgIyBFdmFsdWF0ZSBlYWNoIGh5cGVycGFyYW1ldGVyCgoKICAjIFByZWRpY3RlZCB2YWx1ZSBmcm9tIDUwMCB0cmVlcycgbW9kZWw6CiAgcmZfcHJlZGljdGVkX2JhbGFuY2VkIDwtIGFzLm51bWVyaWMoYXMudmVjdG9yKHJhbmRvbV9mb3Jlc3RfdGVzdChyYW5kb21fZm9yZXN0X2ZpdF81MDBfdHJlZXMsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBkYXRfdGVzdF9iYWxhbmNlZF9yb3NlKSkpCiAgcmZfcHJlZGljdGVkX2ltYmFsYW5jZWQgPC0gYXMubnVtZXJpYyhhcy52ZWN0b3IocmFuZG9tX2ZvcmVzdF90ZXN0KHJhbmRvbV9mb3Jlc3RfZml0XzUwMF90cmVlcywgZGF0X3Rlc3QpKSkKICAjIEV2YWx1YXRlIDUwMCB0cmVlczoKICByZl9hY2N1cmFjeV9iYWxhbmNlZCA8LSBtZWFuKHJvdW5kKHJmX3ByZWRpY3RlZF9iYWxhbmNlZCA9PSBkYXRfdGVzdF9iYWxhbmNlZF9yb3NlJGxhYmVsKSkKICB0cHIuZnByLmJhbGFuY2VkIDwtIFdlaWdodGVkUk9DKGFzLm51bWVyaWMocmZfcHJlZGljdGVkX2JhbGFuY2VkKSxkYXRfdGVzdF9iYWxhbmNlZF9yb3NlJGxhYmVsKQogIHJmX0FVQ19iYWxhbmNlZCA8LSBXZWlnaHRlZEFVQyh0cHIuZnByLmJhbGFuY2VkKQogIHJmX2FjY3VyYWN5X2ltYmFsYW5jZWQgPC0gbWVhbihyb3VuZChyZl9wcmVkaWN0ZWRfaW1iYWxhbmNlZCA9PSBkYXRfdGVzdCRsYWJlbCkpCiAgdHByLmZwci5pbWJhbGFuY2VkIDwtIFdlaWdodGVkUk9DKGFzLm51bWVyaWMocmZfcHJlZGljdGVkX2ltYmFsYW5jZWQpLGRhdF90ZXN0JGxhYmVsKQogIHJmX0FVQ19pbWJhbGFuY2VkIDwtIFdlaWdodGVkQVVDKHRwci5mcHIuaW1iYWxhbmNlZCkKCiAgY2F0KCJBY2N1cmFjeShiYWxhbmNlZCkgNTAwIiwgcmZfYWNjdXJhY3lfYmFsYW5jZWQqMTAwLCIlLlxuIikKICBjYXQoIkFVQyhiYWxhbmNlZCkgNTAwIiwgcmZfQVVDX2JhbGFuY2VkLCIuXG4iKQogIGNhdCgiQWNjdXJhY3koaW1iYWxhbmNlZCkgNTAwIiwgcmZfYWNjdXJhY3lfaW1iYWxhbmNlZCoxMDAsIiUuXG4iKQogIGNhdCgiQVVDKGltYmFsYW5jZWQpIDUwMCIscmZfQVVDX2ltYmFsYW5jZWQsIi5cbiIpCgoKICAjIEV2YWx1YXRpb24gZnJvbSAxMDAwIHRyZWVzJyBtb2RlbDoKICByZl9wcmVkaWN0ZWRfYmFsYW5jZWQgPC0gYXMubnVtZXJpYyhhcy52ZWN0b3IocmFuZG9tX2ZvcmVzdF90ZXN0KHJhbmRvbV9mb3Jlc3RfZml0XzEwMDBfdHJlZXMsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBkYXRfdGVzdF9iYWxhbmNlZF9yb3NlKSkpCiAgcmZfcHJlZGljdGVkX2ltYmFsYW5jZWQgPC0gYXMubnVtZXJpYyhhcy52ZWN0b3IocmFuZG9tX2ZvcmVzdF90ZXN0KHJhbmRvbV9mb3Jlc3RfZml0XzEwMDBfdHJlZXMsIGRhdF90ZXN0KSkpCgogIHJmX2FjY3VyYWN5X2JhbGFuY2VkIDwtIG1lYW4ocm91bmQocmZfcHJlZGljdGVkX2JhbGFuY2VkID09IGRhdF90ZXN0X2JhbGFuY2VkX3Jvc2UkbGFiZWwpKQogIHRwci5mcHIuYmFsYW5jZWQgPC0gV2VpZ2h0ZWRST0MoYXMubnVtZXJpYyhyZl9wcmVkaWN0ZWRfYmFsYW5jZWQpLGRhdF90ZXN0JGxhYmVsKQogIHJmX0FVQ19iYWxhbmNlZCA8LSBXZWlnaHRlZEFVQyh0cHIuZnByLmJhbGFuY2VkKQogIHJmX2FjY3VyYWN5X2ltYmFsYW5jZWQgPC0gbWVhbihyb3VuZChyZl9wcmVkaWN0ZWRfaW1iYWxhbmNlZCA9PSBkYXRfdGVzdF9iYWxhbmNlZF9yb3NlJGxhYmVsKSkKICB0cHIuZnByLmltYmFsYW5jZWQgPC0gV2VpZ2h0ZWRST0MoYXMubnVtZXJpYyhyZl9wcmVkaWN0ZWRfaW1iYWxhbmNlZCksZGF0X3Rlc3QkbGFiZWwpCiAgcmZfQVVDX2ltYmFsYW5jZWQgPC0gV2VpZ2h0ZWRBVUModHByLmZwci5pbWJhbGFuY2VkKQoKICBjYXQoIkFjY3VyYWN5KGJhbGFuY2VkKSAxMDAwIiwgcmZfYWNjdXJhY3lfYmFsYW5jZWQqMTAwLCIlLlxuIikKICBjYXQoIkFVQyhiYWxhbmNlZCkgMTAwMCIsIHJmX0FVQ19iYWxhbmNlZCwiLlxuIikKICBjYXQoIkFjY3VyYWN5KGltYmFsYW5jZWQpIDEwMDAiLCByZl9hY2N1cmFjeV9pbWJhbGFuY2VkKjEwMCwiJS5cbiIpCiAgY2F0KCJBVUMoaW1iYWxhbmNlZCkgMTAwMCIscmZfQVVDX2ltYmFsYW5jZWQsIi5cbiIpCgogICMgMTUwMCB0cmVlcwogIHJmX3ByZWRpY3RlZF9iYWxhbmNlZCA8LSBhcy5udW1lcmljKGFzLnZlY3RvcihyYW5kb21fZm9yZXN0X3Rlc3QocmFuZG9tX2ZvcmVzdF9maXRfMTUwMF90cmVlcywKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGRhdF90ZXN0X2JhbGFuY2VkX3Jvc2UpKSkKICByZl9wcmVkaWN0ZWRfaW1iYWxhbmNlZCA8LSBhcy5udW1lcmljKGFzLnZlY3RvcihyYW5kb21fZm9yZXN0X3Rlc3QocmFuZG9tX2ZvcmVzdF9maXRfMTUwMF90cmVlcywgZGF0X3Rlc3QpKSkKCiAgcmZfYWNjdXJhY3lfYmFsYW5jZWQgPC0gbWVhbihyb3VuZChyZl9wcmVkaWN0ZWRfYmFsYW5jZWQgPT0gZGF0X3Rlc3RfYmFsYW5jZWRfcm9zZSRsYWJlbCkpCiAgdHByLmZwci5iYWxhbmNlZCA8LSBXZWlnaHRlZFJPQyhhcy5udW1lcmljKHJmX3ByZWRpY3RlZF9iYWxhbmNlZCksZGF0X3Rlc3QkbGFiZWwpCiAgcmZfQVVDX2JhbGFuY2VkIDwtIFdlaWdodGVkQVVDKHRwci5mcHIuYmFsYW5jZWQpCiAgcmZfYWNjdXJhY3lfaW1iYWxhbmNlZCA8LSBtZWFuKHJvdW5kKHJmX3ByZWRpY3RlZF9pbWJhbGFuY2VkID09IGRhdF90ZXN0X2JhbGFuY2VkX3Jvc2UkbGFiZWwpKQogIHRwci5mcHIuaW1iYWxhbmNlZCA8LSBXZWlnaHRlZFJPQyhhcy5udW1lcmljKHJmX3ByZWRpY3RlZF9pbWJhbGFuY2VkKSxkYXRfdGVzdCRsYWJlbCkKICByZl9BVUNfaW1iYWxhbmNlZCA8LSBXZWlnaHRlZEFVQyh0cHIuZnByLmltYmFsYW5jZWQpCgogIGNhdCgiQWNjdXJhY3koYmFsYW5jZWQpIDE1MDAiLCByZl9hY2N1cmFjeV9iYWxhbmNlZCoxMDAsIiUuXG4iKQogIGNhdCgiQVVDKGJhbGFuY2VkKSAxNTAwIiwgcmZfQVVDX2JhbGFuY2VkLCIuXG4iKQogIGNhdCgiQWNjdXJhY3koaW1iYWxhbmNlZCkgMTUwMCIsIHJmX2FjY3VyYWN5X2ltYmFsYW5jZWQqMTAwLCIlLlxuIikKICBjYXQoIkFVQyhpbWJhbGFuY2VkKSAxNTAwIixyZl9BVUNfaW1iYWxhbmNlZCwiLlxuIikKCiAgIzIwMDAgdHJlZXMKICByZl9wcmVkaWN0ZWRfYmFsYW5jZWQgPC0gYXMubnVtZXJpYyhhcy52ZWN0b3IocmFuZG9tX2ZvcmVzdF90ZXN0KHJhbmRvbV9mb3Jlc3RfZml0XzIwMDBfdHJlZXMsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBkYXRfdGVzdF9iYWxhbmNlZF9yb3NlKSkpCiAgcmZfcHJlZGljdGVkX2ltYmFsYW5jZWQgPC0gYXMubnVtZXJpYyhhcy52ZWN0b3IocmFuZG9tX2ZvcmVzdF90ZXN0KHJhbmRvbV9mb3Jlc3RfZml0XzIwMDBfdHJlZXMsIGRhdF90ZXN0KSkpCgogIHJmX2FjY3VyYWN5X2JhbGFuY2VkIDwtIG1lYW4ocm91bmQocmZfcHJlZGljdGVkX2JhbGFuY2VkID09IGRhdF90ZXN0X2JhbGFuY2VkX3Jvc2UkbGFiZWwpKQogIHRwci5mcHIuYmFsYW5jZWQgPC0gV2VpZ2h0ZWRST0MoYXMubnVtZXJpYyhyZl9wcmVkaWN0ZWRfYmFsYW5jZWQpLGRhdF90ZXN0JGxhYmVsKQogIHJmX0FVQ19iYWxhbmNlZCA8LSBXZWlnaHRlZEFVQyh0cHIuZnByLmJhbGFuY2VkKQogIHJmX2FjY3VyYWN5X2ltYmFsYW5jZWQgPC0gbWVhbihyb3VuZChyZl9wcmVkaWN0ZWRfaW1iYWxhbmNlZCA9PSBkYXRfdGVzdF9iYWxhbmNlZF9yb3NlJGxhYmVsKSkKICB0cHIuZnByLmltYmFsYW5jZWQgPC0gV2VpZ2h0ZWRST0MoYXMubnVtZXJpYyhyZl9wcmVkaWN0ZWRfaW1iYWxhbmNlZCksZGF0X3Rlc3QkbGFiZWwpCiAgcmZfQVVDX2ltYmFsYW5jZWQgPC0gV2VpZ2h0ZWRBVUModHByLmZwci5pbWJhbGFuY2VkKQoKICBjYXQoIkFjY3VyYWN5KGJhbGFuY2VkKSAyMDAwIiwgcmZfYWNjdXJhY3lfYmFsYW5jZWQqMTAwLCIlLlxuIikKICBjYXQoIkFVQyhiYWxhbmNlZCkgMjAwMCIsIHJmX0FVQ19iYWxhbmNlZCwiLlxuIikKICBjYXQoIkFjY3VyYWN5KGltYmFsYW5jZWQpIDIwMDAiLCByZl9hY2N1cmFjeV9pbWJhbGFuY2VkKjEwMCwiJS5cbiIpCiAgY2F0KCJBVUMoaW1iYWxhbmNlZCkgMjAwMCIscmZfQVVDX2ltYmFsYW5jZWQsIi5cbiIpCgogICMgMjUwMCB0cmVlcwogIHJmX3ByZWRpY3RlZF9iYWxhbmNlZCA8LSBhcy5udW1lcmljKGFzLnZlY3RvcihyYW5kb21fZm9yZXN0X3Rlc3QocmFuZG9tX2ZvcmVzdF9maXRfMjUwMF90cmVlcywKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGRhdF90ZXN0X2JhbGFuY2VkX3Jvc2UpKSkKICByZl9wcmVkaWN0ZWRfaW1iYWxhbmNlZCA8LSBhcy5udW1lcmljKGFzLnZlY3RvcihyYW5kb21fZm9yZXN0X3Rlc3QocmFuZG9tX2ZvcmVzdF9maXRfMjUwMF90cmVlcywgZGF0X3Rlc3QpKSkKCiAgcmZfYWNjdXJhY3lfYmFsYW5jZWQgPC0gbWVhbihyb3VuZChyZl9wcmVkaWN0ZWRfYmFsYW5jZWQgPT0gZGF0X3Rlc3RfYmFsYW5jZWRfcm9zZSRsYWJlbCkpCiAgdHByLmZwci5iYWxhbmNlZCA8LSBXZWlnaHRlZFJPQyhhcy5udW1lcmljKHJmX3ByZWRpY3RlZF9iYWxhbmNlZCksZGF0X3Rlc3QkbGFiZWwpCiAgcmZfQVVDX2JhbGFuY2VkIDwtIFdlaWdodGVkQVVDKHRwci5mcHIuYmFsYW5jZWQpCiAgcmZfYWNjdXJhY3lfaW1iYWxhbmNlZCA8LSBtZWFuKHJvdW5kKHJmX3ByZWRpY3RlZF9pbWJhbGFuY2VkID09IGRhdF90ZXN0X2JhbGFuY2VkX3Jvc2UkbGFiZWwpKQogIHRwci5mcHIuaW1iYWxhbmNlZCA8LSBXZWlnaHRlZFJPQyhhcy5udW1lcmljKHJmX3ByZWRpY3RlZF9pbWJhbGFuY2VkKSxkYXRfdGVzdCRsYWJlbCkKICByZl9BVUNfaW1iYWxhbmNlZCA8LSBXZWlnaHRlZEFVQyh0cHIuZnByLmltYmFsYW5jZWQpCgogIGNhdCgiQWNjdXJhY3koYmFsYW5jZWQpIDI1MDAiLCByZl9hY2N1cmFjeV9iYWxhbmNlZCoxMDAsIiUuXG4iKQogIGNhdCgiQVVDKGJhbGFuY2VkKSAyNTAwIiwgcmZfQVVDX2JhbGFuY2VkLCIuXG4iKQogIGNhdCgiQWNjdXJhY3koaW1iYWxhbmNlZCkgMjUwMCIsIHJmX2FjY3VyYWN5X2ltYmFsYW5jZWQqMTAwLCIlLlxuIikKICBjYXQoIkFVQyhpbWJhbGFuY2VkKSAyNTAwIixyZl9BVUNfaW1iYWxhbmNlZCwiLlxuIikKCiAgIyAxMCBub2RlcwogIHJmX3ByZWRpY3RlZF9iYWxhbmNlZCA8LSBhcy5udW1lcmljKGFzLnZlY3RvcihyYW5kb21fZm9yZXN0X3Rlc3QocmFuZG9tX2ZvcmVzdF9maXRfMTBfbm9kZXMsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBkYXRfdGVzdF9iYWxhbmNlZF9yb3NlKSkpCiAgcmZfcHJlZGljdGVkX2ltYmFsYW5jZWQgPC0gYXMubnVtZXJpYyhhcy52ZWN0b3IocmFuZG9tX2ZvcmVzdF90ZXN0KHJhbmRvbV9mb3Jlc3RfZml0XzEwX25vZGVzLCBkYXRfdGVzdCkpKQoKICByZl9hY2N1cmFjeV9iYWxhbmNlZCA8LSBtZWFuKHJvdW5kKHJmX3ByZWRpY3RlZF9iYWxhbmNlZCA9PSBkYXRfdGVzdF9iYWxhbmNlZF9yb3NlJGxhYmVsKSkKICB0cHIuZnByLmJhbGFuY2VkIDwtIFdlaWdodGVkUk9DKGFzLm51bWVyaWMocmZfcHJlZGljdGVkX2JhbGFuY2VkKSxkYXRfdGVzdCRsYWJlbCkKICByZl9BVUNfYmFsYW5jZWQgPC0gV2VpZ2h0ZWRBVUModHByLmZwci5iYWxhbmNlZCkKICByZl9hY2N1cmFjeV9pbWJhbGFuY2VkIDwtIG1lYW4ocm91bmQocmZfcHJlZGljdGVkX2ltYmFsYW5jZWQgPT0gZGF0X3Rlc3RfYmFsYW5jZWRfcm9zZSRsYWJlbCkpCiAgdHByLmZwci5pbWJhbGFuY2VkIDwtIFdlaWdodGVkUk9DKGFzLm51bWVyaWMocmZfcHJlZGljdGVkX2ltYmFsYW5jZWQpLGRhdF90ZXN0JGxhYmVsKQogIHJmX0FVQ19pbWJhbGFuY2VkIDwtIFdlaWdodGVkQVVDKHRwci5mcHIuaW1iYWxhbmNlZCkKCiAgY2F0KCJBY2N1cmFjeShiYWxhbmNlZCkgMTAiLCByZl9hY2N1cmFjeV9iYWxhbmNlZCoxMDAsIiUuXG4iKQogIGNhdCgiQVVDKGJhbGFuY2VkKSAxMCIsIHJmX0FVQ19iYWxhbmNlZCwiLlxuIikKICBjYXQoIkFjY3VyYWN5KGltYmFsYW5jZWQpIDEwIiwgcmZfYWNjdXJhY3lfaW1iYWxhbmNlZCoxMDAsIiUuXG4iKQogIGNhdCgiQVVDKGltYmFsYW5jZWQpIDEwIixyZl9BVUNfaW1iYWxhbmNlZCwiLlxuIikKCiAgIyAxNSBub2RlcwogIHJmX3ByZWRpY3RlZF9iYWxhbmNlZCA8LSBhcy5udW1lcmljKGFzLnZlY3RvcihyYW5kb21fZm9yZXN0X3Rlc3QocmFuZG9tX2ZvcmVzdF9maXRfMTVfbm9kZXMsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBkYXRfdGVzdF9iYWxhbmNlZF9yb3NlKSkpCiAgcmZfcHJlZGljdGVkX2ltYmFsYW5jZWQgPC0gYXMubnVtZXJpYyhhcy52ZWN0b3IocmFuZG9tX2ZvcmVzdF90ZXN0KHJhbmRvbV9mb3Jlc3RfZml0XzE1X25vZGVzLCBkYXRfdGVzdCkpKQoKICByZl9hY2N1cmFjeV9iYWxhbmNlZCA8LSBtZWFuKHJvdW5kKHJmX3ByZWRpY3RlZF9iYWxhbmNlZCA9PSBkYXRfdGVzdF9iYWxhbmNlZF9yb3NlJGxhYmVsKSkKICB0cHIuZnByLmJhbGFuY2VkIDwtIFdlaWdodGVkUk9DKGFzLm51bWVyaWMocmZfcHJlZGljdGVkX2JhbGFuY2VkKSxkYXRfdGVzdCRsYWJlbCkKICByZl9BVUNfYmFsYW5jZWQgPC0gV2VpZ2h0ZWRBVUModHByLmZwci5iYWxhbmNlZCkKICByZl9hY2N1cmFjeV9pbWJhbGFuY2VkIDwtIG1lYW4ocm91bmQocmZfcHJlZGljdGVkX2ltYmFsYW5jZWQgPT0gZGF0X3Rlc3RfYmFsYW5jZWRfcm9zZSRsYWJlbCkpCiAgdHByLmZwci5pbWJhbGFuY2VkIDwtIFdlaWdodGVkUk9DKGFzLm51bWVyaWMocmZfcHJlZGljdGVkX2ltYmFsYW5jZWQpLGRhdF90ZXN0JGxhYmVsKQogIHJmX0FVQ19pbWJhbGFuY2VkIDwtIFdlaWdodGVkQVVDKHRwci5mcHIuaW1iYWxhbmNlZCkKCiAgY2F0KCJBY2N1cmFjeShiYWxhbmNlZCkgMTUiLCByZl9hY2N1cmFjeV9iYWxhbmNlZCoxMDAsIiUuXG4iKQogIGNhdCgiQVVDKGJhbGFuY2VkKSAxNSIsIHJmX0FVQ19iYWxhbmNlZCwiLlxuIikKICBjYXQoIkFjY3VyYWN5KGltYmFsYW5jZWQpIDE1IiwgcmZfYWNjdXJhY3lfaW1iYWxhbmNlZCoxMDAsIiUuXG4iKQogIGNhdCgiQVVDKGltYmFsYW5jZWQpIDE1IixyZl9BVUNfaW1iYWxhbmNlZCwiLlxuIikKCiAgIyAyMCBub2RlcwogIHJmX3ByZWRpY3RlZF9iYWxhbmNlZCA8LSBhcy5udW1lcmljKGFzLnZlY3RvcihyYW5kb21fZm9yZXN0X3Rlc3QocmFuZG9tX2ZvcmVzdF9maXRfMjBfbm9kZXMsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBkYXRfdGVzdF9iYWxhbmNlZF9yb3NlKSkpCiAgcmZfcHJlZGljdGVkX2ltYmFsYW5jZWQgPC0gYXMubnVtZXJpYyhhcy52ZWN0b3IocmFuZG9tX2ZvcmVzdF90ZXN0KHJhbmRvbV9mb3Jlc3RfZml0XzIwX25vZGVzLCBkYXRfdGVzdCkpKQoKICByZl9hY2N1cmFjeV9iYWxhbmNlZCA8LSBtZWFuKHJvdW5kKHJmX3ByZWRpY3RlZF9iYWxhbmNlZCA9PSBkYXRfdGVzdF9iYWxhbmNlZF9yb3NlJGxhYmVsKSkKICB0cHIuZnByLmJhbGFuY2VkIDwtIFdlaWdodGVkUk9DKGFzLm51bWVyaWMocmZfcHJlZGljdGVkX2JhbGFuY2VkKSxkYXRfdGVzdCRsYWJlbCkKICByZl9BVUNfYmFsYW5jZWQgPC0gV2VpZ2h0ZWRBVUModHByLmZwci5iYWxhbmNlZCkKICByZl9hY2N1cmFjeV9pbWJhbGFuY2VkIDwtIG1lYW4ocm91bmQocmZfcHJlZGljdGVkX2ltYmFsYW5jZWQgPT0gZGF0X3Rlc3RfYmFsYW5jZWRfcm9zZSRsYWJlbCkpCiAgdHByLmZwci5pbWJhbGFuY2VkIDwtIFdlaWdodGVkUk9DKGFzLm51bWVyaWMocmZfcHJlZGljdGVkX2ltYmFsYW5jZWQpLGRhdF90ZXN0JGxhYmVsKQogIHJmX0FVQ19pbWJhbGFuY2VkIDwtIFdlaWdodGVkQVVDKHRwci5mcHIuaW1iYWxhbmNlZCkKCiAgY2F0KCJBY2N1cmFjeShiYWxhbmNlZCkgMjAiLCByZl9hY2N1cmFjeV9iYWxhbmNlZCoxMDAsIiUuXG4iKQogIGNhdCgiQVVDKGJhbGFuY2VkKSAyMCIsIHJmX0FVQ19iYWxhbmNlZCwiLlxuIikKICBjYXQoIkFjY3VyYWN5KGltYmFsYW5jZWQpIDIwIiwgcmZfYWNjdXJhY3lfaW1iYWxhbmNlZCoxMDAsIiUuXG4iKQogIGNhdCgiQVVDKGltYmFsYW5jZWQpIDIwIixyZl9BVUNfaW1iYWxhbmNlZCwiLlxuIikKCiAgIyAyNSBub2RlcwogIHJmX3ByZWRpY3RlZF9iYWxhbmNlZCA8LSBhcy5udW1lcmljKGFzLnZlY3RvcihyYW5kb21fZm9yZXN0X3Rlc3QocmFuZG9tX2ZvcmVzdF9maXRfMjVfbm9kZXMsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBkYXRfdGVzdF9iYWxhbmNlZF9yb3NlKSkpCiAgcmZfcHJlZGljdGVkX2ltYmFsYW5jZWQgPC0gYXMubnVtZXJpYyhhcy52ZWN0b3IocmFuZG9tX2ZvcmVzdF90ZXN0KHJhbmRvbV9mb3Jlc3RfZml0XzI1X25vZGVzLCBkYXRfdGVzdCkpKQoKICByZl9hY2N1cmFjeV9iYWxhbmNlZCA8LSBtZWFuKHJvdW5kKHJmX3ByZWRpY3RlZF9iYWxhbmNlZCA9PSBkYXRfdGVzdF9iYWxhbmNlZF9yb3NlJGxhYmVsKSkKICB0cHIuZnByLmJhbGFuY2VkIDwtIFdlaWdodGVkUk9DKGFzLm51bWVyaWMocmZfcHJlZGljdGVkX2JhbGFuY2VkKSxkYXRfdGVzdCRsYWJlbCkKICByZl9BVUNfYmFsYW5jZWQgPC0gV2VpZ2h0ZWRBVUModHByLmZwci5iYWxhbmNlZCkKICByZl9hY2N1cmFjeV9pbWJhbGFuY2VkIDwtIG1lYW4ocm91bmQocmZfcHJlZGljdGVkX2ltYmFsYW5jZWQgPT0gZGF0X3Rlc3RfYmFsYW5jZWRfcm9zZSRsYWJlbCkpCiAgdHByLmZwci5pbWJhbGFuY2VkIDwtIFdlaWdodGVkUk9DKGFzLm51bWVyaWMocmZfcHJlZGljdGVkX2ltYmFsYW5jZWQpLGRhdF90ZXN0JGxhYmVsKQogIHJmX0FVQ19pbWJhbGFuY2VkIDwtIFdlaWdodGVkQVVDKHRwci5mcHIuaW1iYWxhbmNlZCkKCiAgY2F0KCJBY2N1cmFjeShiYWxhbmNlZCkgMjUiLCByZl9hY2N1cmFjeV9iYWxhbmNlZCoxMDAsIiUuXG4iKQogIGNhdCgiQVVDKGJhbGFuY2VkKSAyNSIsIHJmX0FVQ19iYWxhbmNlZCwiLlxuIikKICBjYXQoIkFjY3VyYWN5KGltYmFsYW5jZWQpIDI1IiwgcmZfYWNjdXJhY3lfaW1iYWxhbmNlZCoxMDAsIiUuXG4iKQogIGNhdCgiQVVDKGltYmFsYW5jZWQpIDI1IixyZl9BVUNfaW1iYWxhbmNlZCwiLlxuIikKCiAgIyAzMCBub2RlcwogIHJmX3ByZWRpY3RlZF9iYWxhbmNlZCA8LSBhcy5udW1lcmljKGFzLnZlY3RvcihyYW5kb21fZm9yZXN0X3Rlc3QocmFuZG9tX2ZvcmVzdF9maXRfMzBfbm9kZXMsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBkYXRfdGVzdF9iYWxhbmNlZF9yb3NlKSkpCiAgcmZfcHJlZGljdGVkX2ltYmFsYW5jZWQgPC0gYXMubnVtZXJpYyhhcy52ZWN0b3IocmFuZG9tX2ZvcmVzdF90ZXN0KHJhbmRvbV9mb3Jlc3RfZml0XzMwX25vZGVzLCBkYXRfdGVzdCkpKQoKICByZl9hY2N1cmFjeV9iYWxhbmNlZCA8LSBtZWFuKHJvdW5kKHJmX3ByZWRpY3RlZF9iYWxhbmNlZCA9PSBkYXRfdGVzdF9iYWxhbmNlZF9yb3NlJGxhYmVsKSkKICB0cHIuZnByLmJhbGFuY2VkIDwtIFdlaWdodGVkUk9DKGFzLm51bWVyaWMocmZfcHJlZGljdGVkX2JhbGFuY2VkKSxkYXRfdGVzdCRsYWJlbCkKICByZl9BVUNfYmFsYW5jZWQgPC0gV2VpZ2h0ZWRBVUModHByLmZwci5iYWxhbmNlZCkKICByZl9hY2N1cmFjeV9pbWJhbGFuY2VkIDwtIG1lYW4ocm91bmQocmZfcHJlZGljdGVkX2ltYmFsYW5jZWQgPT0gZGF0X3Rlc3RfYmFsYW5jZWRfcm9zZSRsYWJlbCkpCiAgdHByLmZwci5pbWJhbGFuY2VkIDwtIFdlaWdodGVkUk9DKGFzLm51bWVyaWMocmZfcHJlZGljdGVkX2ltYmFsYW5jZWQpLGRhdF90ZXN0JGxhYmVsKQogIHJmX0FVQ19pbWJhbGFuY2VkIDwtIFdlaWdodGVkQVVDKHRwci5mcHIuaW1iYWxhbmNlZCkKCiAgY2F0KCJBY2N1cmFjeShiYWxhbmNlZCkgMzAiLCByZl9hY2N1cmFjeV9iYWxhbmNlZCoxMDAsIiUuXG4iKQogIGNhdCgiQVVDKGJhbGFuY2VkKSAzMCIsIHJmX0FVQ19iYWxhbmNlZCwiLlxuIikKICBjYXQoIkFjY3VyYWN5KGltYmFsYW5jZWQpIDMwIiwgcmZfYWNjdXJhY3lfaW1iYWxhbmNlZCoxMDAsIiUuXG4iKQogIGNhdCgiQVVDKGltYmFsYW5jZWQpIDMwIixyZl9BVUNfaW1iYWxhbmNlZCwiLlxuIikKfQpgYGAKCioqMTUgTm9kZXMgYW5kIDI1MDAgdHJlZXMgaXMgdGhlIGJlc3QuKioKCiogVHJhaW4gUmFuZG9tIEZvcmVzdCB3aXRoIFR1bmVkIFBhcmFtZXRlcnMKCmBgYHtyfQppZihydW4ucmYpewogIGlmKHRyYWluLnJhbmRvbS5mb3Jlc3QpewogICAgdGltZS5yZi50cmFpbi5maW5hbC5iYWxhbmNlZCA8LSBzeXN0ZW0udGltZShyYW5kb21fZm9yZXN0X2ZpdF9maW5hbF9iYWxhbmNlZCA8LQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHJhbmRvbV9mb3Jlc3RfdHJhaW4oZGF0X3RyYWluX2JhbGFuY2VkX3Jvc2UsIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgbXRyeSA9IDMwOCwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHRyZWVfbnVtYmVyID0gMjAwMCwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBub2RlX3NpemUgPSAxNSkpCiAgICBzYXZlKHJhbmRvbV9mb3Jlc3RfZml0X2ZpbmFsX2JhbGFuY2VkLCBmaWxlID0gIi4uL291dHB1dC9yZl90cmFpbl9maW5hbF9iYWxhbmNlZF9vbGRfZmVhdHVyZS5SRGF0YSIpCiAgICBzYXZlKHRpbWUucmYudHJhaW4uZmluYWwuYmFsYW5jZWQsIGZpbGUgPSAiLi4vb3V0cHV0L3JmX3RyYWluX2ZpbmFsX3RpbWVfYmFsYW5jZWRfb2xkX2ZlYXR1cmUuUkRhdGEiKQogICAgdGltZS5yZi50cmFpbi5maW5hbC5pbWJhbGFuY2VkIDwtIHN5c3RlbS50aW1lKHJhbmRvbV9mb3Jlc3RfZml0X2ZpbmFsX2ltYmFsYW5jZWQgPC0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICByYW5kb21fZm9yZXN0X3RyYWluKGRhdF90cmFpbiwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBtdHJ5ID0gMzA4LCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHRyZWVfbnVtYmVyID0gMTAwMCwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIG5vZGVfc2l6ZSA9IDE1KSkKICAgIHNhdmUodGltZS5yZi50cmFpbi5maW5hbC5pbWJhbGFuY2VkLCBmaWxlID0gIi4uL291dHB1dC9yZl90cmFpbl9maW5hbF90aW1lX2ltYmFsYW5jZWRfb2xkX2ZlYXR1cmUuUkRhdGEiKQogICAgc2F2ZShyYW5kb21fZm9yZXN0X2ZpdF9maW5hbF9pbWJhbGFuY2VkLCBmaWxlID0gIi4uL291dHB1dC9yZl90cmFpbl9maW5hbF9pbWJhbGFuY2VkX29sZF9mZWF0dXJlLlJEYXRhIikKICB9IGVsc2UgewogICAgbG9hZCgiLi4vb3V0cHV0L3JmX3RyYWluX2ZpbmFsX2JhbGFuY2VkX29sZF9mZWF0dXJlLlJEYXRhIikKICAgIGxvYWQoIi4uL291dHB1dC9yZl90cmFpbl9maW5hbF90aW1lX2JhbGFuY2VkX29sZF9mZWF0dXJlLlJEYXRhIikKICAgIGxvYWQoIi4uL291dHB1dC9yZl90cmFpbl9maW5hbF90aW1lX2ltYmFsYW5jZWRfb2xkX2ZlYXR1cmUuUkRhdGEiKQogICAgbG9hZCgiLi4vb3V0cHV0L3JmX3RyYWluX2ZpbmFsX2ltYmFsYW5jZWRfb2xkX2ZlYXR1cmUuUkRhdGEiKQogIH0KfQpgYGAKCiogVGVzdCBhbmQgRXZhbHVhdGUgUmFuZG9tIEZvcmVzdCB3aXRoIFR1bmVkIFBhcmFtZXRlcnMKCmBgYHtyfQppZihydW4ucmYpewogICMgQmFsYW5jZWQ6CiAgaWYodGVzdC5yYW5kb20uZm9yZXN0KXsKICB0aW1lLnJmLnRlc3QuZmluYWwuYmFsYW5jZWQgPC0gc3lzdGVtLnRpbWUoCiAgICByZl9wcmVkaWN0ZWRfYmFsYW5jZWQgPC0gYXMubnVtZXJpYyhhcy52ZWN0b3IocmFuZG9tX2ZvcmVzdF90ZXN0KHJhbmRvbV9mb3Jlc3RfZml0X2ZpbmFsX2JhbGFuY2VkLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBkYXRfdGVzdF9iYWxhbmNlZF9yb3NlKSkpKQogIHJmX2FjY3VyYWN5X2JhbGFuY2VkIDwtIG1lYW4ocm91bmQocmZfcHJlZGljdGVkX2JhbGFuY2VkID09IGRhdF90ZXN0X2JhbGFuY2VkX3Jvc2UkbGFiZWwpKQogIHRwci5mcHIuYmFsYW5jZWQgPC0gV2VpZ2h0ZWRST0MoYXMubnVtZXJpYyhyZl9wcmVkaWN0ZWRfYmFsYW5jZWQpLGRhdF90ZXN0X2JhbGFuY2VkX3Jvc2UkbGFiZWwpCiAgcmZfQVVDX2JhbGFuY2VkIDwtIFdlaWdodGVkQVVDKHRwci5mcHIuYmFsYW5jZWQpCgogIGNhdCgiQVVDKGJhbGFuY2VkKTogIiwgcmZfQVVDX2JhbGFuY2VkLCIuXG4iKQogIGNhdCgiQWNjdXJhY3koYmFsYW5jZWQpIiwgcmZfYWNjdXJhY3lfYmFsYW5jZWQqMTAwLCIlLlxuIikKICBjYXQoIlRyYWluaW5nIHRpbWU6ICIsIHRpbWUucmYudHJhaW4uZmluYWwuYmFsYW5jZWRbMV0sICJzLlxuIikKICBjYXQoIlRlc3RpbmcgdGltZTogIiwgdGltZS5yZi50ZXN0LmZpbmFsLmJhbGFuY2VkWzFdLCAicy5cbiIpCgogICMgSW1iYWxhbmNlZDoKICB0aW1lLnJmLnRlc3QuZmluYWwuaW1iYWxhbmNlZCA8LSBzeXN0ZW0udGltZSgKICAgIHJmX3ByZWRpY3RlZF9pbWJhbGFuY2VkIDwtYXMubnVtZXJpYyhhcy52ZWN0b3IocmFuZG9tX2ZvcmVzdF90ZXN0KHJhbmRvbV9mb3Jlc3RfZml0X2ZpbmFsX2ltYmFsYW5jZWQsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBkYXRfdGVzdCkpKSkKICByZl9hY2N1cmFjeV9pbWJhbGFuY2VkIDwtIG1lYW4ocm91bmQocmZfcHJlZGljdGVkX2ltYmFsYW5jZWQgPT0gZGF0X3Rlc3QkbGFiZWwpKQogIHRwci5mcHIuaW1iYWxhbmNlZCA8LSBXZWlnaHRlZFJPQyhhcy5udW1lcmljKHJmX3ByZWRpY3RlZF9pbWJhbGFuY2VkKSxkYXRfdGVzdCRsYWJlbCkKICByZl9BVUNfaW1iYWxhbmNlZCA8LSBXZWlnaHRlZEFVQyh0cHIuZnByLmltYmFsYW5jZWQpCgogIGNhdCgiQVVDKGltYmFsYW5jZWQpOiAiLCByZl9BVUNfaW1iYWxhbmNlZCwiLlxuIikKICBjYXQoIkFjY3VyYWN5KGltYmFsYW5jZWQpIiwgcmZfYWNjdXJhY3lfaW1iYWxhbmNlZCoxMDAsIiUuXG4iKQogIGNhdCgiVHJhaW5pbmcgdGltZTogIiwgdGltZS5yZi50cmFpbi5maW5hbC5pbWJhbGFuY2VkWzFdLCAicy5cbiIpCiAgY2F0KCJUZXN0aW5nIHRpbWU6ICIsIHRpbWUucmYudGVzdC5maW5hbC5pbWJhbGFuY2VkWzFdLCAicy5cbiIpCiAgfQp9CmBgYAoKLS0tLS0tLS1USElTIElTIFRPIFNFUEFSQVRFIEVBQ0ggTU9ERUwuIFRISVMgSVMgVE8gU0VQQVJBVEUgRUFDSCBNT0RFTC4gVEhJUyBJUyBUTyBTRVBBUkFURSBFQUNIIE1PREVMLi0tLS0tLS0tLS0KCgoKCiMgQWR2YW5jZWQgTW9kZWwgMzogU1ZNIE1vZGVsCgoqIEJhbGFuY2UgdGhlIFRyYWluaW5nIFNldAoKSWYgdGhlIGdpdmVuIGRhdGEgc2V0IGlzIGltYmFsYW5jZWQsIHdlIGFwcGx5IFNNT1RFIHRvIHJlYmFsYW5jZSB0aGUgZGF0YSBhbmQgdXNlIGl0IHRvIHRyYWluIG91ciBzdm0gbW9kZWxzLgoKYGBge3J9CmlmKHJ1bi5zdm0pewogIHRtX3N2bV9yZWJhbGFuY2VkX3RyYWluIDwtIE5BCiAgaWYoc2FtcGxlLnJld2VpZ2h0KXsKICAgIGRhdF90cmFpbiRsYWJlbCA9IGFzLmZhY3RvcihkYXRfdHJhaW4kbGFiZWwpCiAgICB0bV9zdm1fcmViYWxhbmNlZF90cmFpbiA8LSBzeXN0ZW0udGltZShzdm1fdHJhaW5pbmdfZGF0YSA8LSBTTU9URShsYWJlbCB+IC4sIGRhdGEgPSBkYXRfdHJhaW4pKQogICAgc2F2ZSh0bV9zdm1fcmViYWxhbmNlZF90cmFpbiwgZmlsZT0iLi4vb3V0cHV0L3RtX3N2bV9yZWJhbGFuY2VkX3RyYWluLlJEYXRhIikKICB9IGVsc2UgewogICAgc3ZtX3RyYWluaW5nX2RhdGEgPC0gZGF0X3RyYWluCiAgICB0bV9zdm1fcmViYWxhbmNlZF90cmFpbiA8LSB0bV9mZWF0dXJlX3RyYWluCiAgfQp9CmBgYAoKKiBNb2RlbCBTZWxlY3Rpb24KClR1bmluZyBoeXBlcnBhcmFtZXRlcnMgZm9yIGJvdGggbGluZWFyIGFuZCByYWRpYWwgYmFzaXMga2VybmVsIGFuZCBzZWxlY3QgdGhlIGtlcm5lbCBtZXRob2QgdGhhdCBwcm9kdWNlcyB0aGUgaGlnaGVzdCBBVUMgYW5kIGFjY3VyYWN5IGFtb25nIHRoZSB0d28gbWV0aG9kcy4KCmBgYHtyfQppZihydW4uc3ZtKXsKICBzZXQuc2VlZCgyMDIxKQogIHRtX3N2bV9saW5lYXJfbW9kIDwtIE5BCiAgdG1fc3ZtX3JhZGlhbF9tb2QgPC0gTkEKICBpZihtb2RlbC5zZWxlY3Rpb24pewogICAgc3ZtX21vZGVsX2F1YyA8LSByZXAoTkEsIDIpCiAgICBzdm1fbW9kZWxfYWNjdSA8LSByZXAoTkEsIDIpCiAgICAjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMgbGluZWFyIGtlcm5lbAogICAgaWYocnVuLmN2KXsKICAgICAgYmVzdC5saW5lYXIuY29zdCA8LSBzdm1fbGluZWFyX2Nvc3RfdHVuZShzdm1fdHJhaW5pbmdfZGF0YSkKICAgICAgY2F0KCJUaGUgYmVzdCBjb3N0IGZvciBzdm0gbW9kZWwgd2l0aCBsaW5lYXIga2VybmVsIGlzOiAiLCBiZXN0LmxpbmVhci5jb3N0JGJlc3QucGFyYW1ldGVycyRjb3N0KQogICAgICBzdm0ubGluZWFyLnRyYWluLnN0YXJ0ID0gcHJvYy50aW1lKCkKICAgICAgdG1fc3ZtX2xpbmVhcl9tb2QgPC0gc3lzdGVtLnRpbWUoc3ZtX2xpbmVhcl9tb2QgPC0gc3ZtX2xpbmVhcl90cmFpbihzdm1fdHJhaW5pbmdfZGF0YSwgMC4wMSwgSykpCiAgICAgIHN2bS5saW5lYXIudHJhaW4uZW5kID0gcHJvYy50aW1lKCkKICAgICAgc3ZtLmxpbmVhci50bSA9IHN2bS5saW5lYXIudHJhaW4uZW5kIC0gc3ZtLmxpbmVhci50cmFpbi5zdGFydAogICAgICBzYXZlKHN2bV9saW5lYXJfbW9kLCBmaWxlPSIuLi9vdXRwdXQvc3ZtX2xpbmVhcl9tb2QuUkRhdGEiKQogICAgICBzYXZlKHN2bS5saW5lYXIudG1bWzNdXSwgZmlsZT0iLi4vb3V0cHV0L3RtX3N2bV9saW5lYXJfbW9kLlJEYXRhIikKICAgIH0KICAgIHN2bV9saW5lYXJfcHJlZCA8LSBzdm1fdGVzdChzdm1fbGluZWFyX21vZCwgc3ZtX3RyYWluaW5nX2RhdGEsIEZBTFNFKQogICAgIyBldmFsdWF0ZSBwZXJmb3JtYW5jZSBvbiBsaW5lYXIga2VybmVsCiAgICBzdm1fbW9kZWxfYWNjdVsxXSA8LSBtZWFuKHJvdW5kKHN2bV9saW5lYXJfcHJlZCA9PSBzdm1fdHJhaW5pbmdfZGF0YSRsYWJlbCkpCiAgICB0cHIuZnByX2xpbmVhciA8LSBXZWlnaHRlZFJPQyhhcy5udW1lcmljKHN2bV9saW5lYXJfcHJlZCksIHN2bV90cmFpbmluZ19kYXRhJGxhYmVsKQogICAgc3ZtX21vZGVsX2F1Y1sxXSA8LSBXZWlnaHRlZEFVQyh0cHIuZnByX2xpbmVhcikKICAgIAogICAgIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMgcmFkaWFsIGJhc2lzIGtlcm5lbAogICAgaWYocnVuLmN2KXsKICAgICAgI2Jlc3QucmFkaWFsLmNvc3QgPC0gc3ZtX3JhZGlhbF9jb3N0X3R1bmUoc3ZtX3RyYWluaW5nX2RhdGEpCiAgICAgIHN2bS5yYmYudHJhaW4uc3RhcnQgPSBwcm9jLnRpbWUoKQogICAgICB0bV9zdm1fcmFkaWFsX21vZCA8IHN5c3RlbS50aW1lKHN2bV9yYWRpYWxfbW9kIDwtIHN2bV9yYWRpYWxfdHJhaW4oc3ZtX3RyYWluaW5nX2RhdGEsIDEsIEspKQogICAgICBzdm0ucmJmLnRyYWluLmVuZCA9IHByb2MudGltZSgpCiAgICAgIHN2bS5yYmYudG0gPSBzdm0ucmJmLnRyYWluLmVuZCAtIHN2bS5yYmYudHJhaW4uc3RhcnQKICAgICAgc2F2ZShzdm1fcmFkaWFsX21vZCwgZmlsZT0iLi4vb3V0cHV0L3N2bV9yYWRpYWxfbW9kLlJEYXRhIikKICAgICAgc2F2ZShzdm0ucmJmLnRtW1szXV0sIGZpbGU9Ii4uL291dHB1dC90bV9zdm1fcmFkaWFsX21vZC5SRGF0YSIpCiAgICB9CiAgICBzdm1fcmFkaWFsX3ByZWQgPC0gc3ZtX3Rlc3Qoc3ZtX3JhZGlhbF9tb2QsIHN2bV90cmFpbmluZ19kYXRhLCBGQUxTRSkKICAgICMgZXZhbHVhdGUgcGVyZm9ybWFuY2Ugb24gcmJmIGtlcm5lbAogICAgc3ZtX21vZGVsX2FjY3VbMl0gPC0gbWVhbihyb3VuZChzdm1fcmFkaWFsX3ByZWQgPT0gc3ZtX3RyYWluaW5nX2RhdGEkbGFiZWwpKQogICAgdHByLmZwcl9kZWZhdWx0IDwtIFdlaWdodGVkUk9DKGFzLm51bWVyaWMoc3ZtX3JhZGlhbF9wcmVkKSwgc3ZtX3RyYWluaW5nX2RhdGEkbGFiZWwpCiAgICBzdm1fbW9kZWxfYXVjWzJdIDwtIFdlaWdodGVkQVVDKHRwci5mcHJfZGVmYXVsdCkKICAgIAogICAgIyB0YWJsZSB0byBkaXNwbGF5IHJlc3VsdHMgZm9yIHRoZSB0d28ga2VybmVsIG1ldGhvZHMKICAgIHN2bV9yZXMgPSBtYXRyaXgocmVwKE5BLDYpLG5jb2w9MykKICAgIHN2bV9yZXNbLDFdID0gc3ZtX21vZGVsX2FjY3UKICAgIHN2bV9yZXNbLDJdID0gc3ZtX21vZGVsX2F1YwogICAgc3ZtX3Jlc1ssM10gPSBjKHN2bS5saW5lYXIudG1bM10sIHN2bS5yYmYudG1bM10pCiAgICBjb2xuYW1lcyhzdm1fcmVzKSA9IGMoIkFjY3VyYWN5IiwgIkFVQyIsIlJ1bm5pbmcgVGltZSIpIAogICAgcm93bmFtZXMoc3ZtX3JlcykgPSBjKCJsaW5lYXIiLCJyYWRpYWwgYmFzaXMiKQogICAgc2F2ZShzdm1fcmVzLCBmaWxlPSIuLi9vdXRwdXQvc3ZtX21vZGVsX3NlbGVjdGlvbi5SRGF0YSIpCiAgfSBlbHNlIHsKICAgIGlmKCFydW4ucHJlc2VudGF0aW9uLmRheSl7CiAgICAgIHRtX3N2bV9yYWRpYWxfbW9kIDwgc3lzdGVtLnRpbWUoc3ZtX3JhZGlhbF9tb2QgPC0gc3ZtX3JhZGlhbF90cmFpbihzdm1fdHJhaW5pbmdfZGF0YSwgMSwgSykpCiAgICB9IGVsc2UgewogICAgICBsb2FkKGZpbGU9Ii4uL291dHB1dC9zdm1fcmFkaWFsX21vZC5SRGF0YSIpCiAgICAgIGxvYWQoZmlsZT0iLi4vb3V0cHV0L3N2bV9tb2RlbF9zZWxlY3Rpb24uUkRhdGEiKQogICAgICBzdm1fcmVzCiAgICB9CiAgfQp9CmBgYAoKCgoqKlNpbmNlIHJhZGlhbCBiYXNpcyBrZXJuZWwgaGFzIGhpZ2hlciBhY2N1cmFjeSBhbmQgQVVDIHRoYW4gbGluZWFyIGtlcm5lbCwgd2Ugd2lsbCBjaG9vc2UgcmFkaWFsIGJhc2lzIGFzIG91ciBrZXJuZWwgbWV0aG9kIGZvciB0cmFpbmluZyB0aGUgc3ZtIG1vZGVsLioqCiAgCiAgKiBFdmFsdWF0aW9uIG9uIFRlc3RpbmcgRGF0YQoKYGBge3J9CmlmKHJ1bi5zdm0pewogIHN2bV90ZXN0aW5nX2RhdGEgPC0gZGF0X3Rlc3QKICBpZihydW4uc3ZtLnRlc3QpewogICAgIyMgcmJmCiAgICB0bV9zdm1fcmJmX3Rlc3QgPC0gc3lzdGVtLnRpbWUoc3ZtX3JiZl9wcmVkIDwtIHN2bV90ZXN0KHN2bV9yYWRpYWxfbW9kLCBzdm1fdGVzdGluZ19kYXRhLCBGQUxTRSkpCiAgICBzdm1fdGVzdF9hY2N1ID0gbWVhbihyb3VuZChzdm1fcmJmX3ByZWQgPT0gc3ZtX3Rlc3RpbmdfZGF0YSRsYWJlbCkpCiAgICB0cHIuZnByLnJiZiA8LSBXZWlnaHRlZFJPQyhhcy5udW1lcmljKHN2bV9yYmZfcHJlZCksIHN2bV90ZXN0aW5nX2RhdGEkbGFiZWwpCiAgICBzdm1fdGVzdF9hdWMgPSBXZWlnaHRlZEFVQyh0cHIuZnByLnJiZikKICAgIHNhdmUodG1fc3ZtX3JiZl90ZXN0LCBmaWxlPSIuLi9vdXRwdXQvdG1fc3ZtX3JiZl90ZXN0LlJEYXRhIikKICAgIAogICAgY2F0KCJUaGUgYWNjdXJhY3kgb2Ygc3ZtIG1vZGVsIGlzIiwgc3ZtX3Rlc3RfYWNjdSoxMDAsICIlLlxuIikKICAgIGNhdCgiVGhlIEFVQyBvZiBzdm0gbW9kZWwgaXMiLCBzdm1fdGVzdF9hdWMsICIuXG4iKQogIH0KfQpgYGAKCgoKCiogU3VtbWFyaXplIFJ1bm5pbmcgVGltZQoKYGBge3J9CmlmKHJ1bi5zdm0pewogIGNhdCgiVGltZSBmb3IgdHJhaW5pbmcgc3ZtIG1vZGVsID0iLCBzdm0ucmJmLnRtW1szXV0sICJzIFxuIikKICBjYXQoIlRpbWUgZm9yIHRlc3Rpbmcgc3ZtIG1vZGVsPSIsIHRtX3N2bV9yYmZfdGVzdFsxXSwgInMgXG4iKQp9CmBgYAoKLS0tLS0tLS1USElTIElTIFRPIFNFUEFSQVRFIEVBQ0ggTU9ERUwuIFRISVMgSVMgVE8gU0VQQVJBVEUgRUFDSCBNT0RFTC4gVEhJUyBJUyBUTyBTRVBBUkFURSBFQUNIIE1PREVMLi0tLS0tLS0tLS0KCgoKCiMgQWR2YW5jZWQgTW9kZWwgNDogUmlkZ2UgTW9kZWwKCiogQXBwbHkgQ29uc3RydWN0ZWQgUmlkZ2UgTW9kZWwgdG8gdGhlIFRyYWluaW5nIFNldAoKYGBge3J9CmlmKHJ1bi5yaWRnZSl7CiAgdG1fcmlkZ2VfdHJhaW4gPC0gTkEKICBpZiAodHJhaW4ucmlkZ2UpewogICAgZGF0X3RyYWluX3JlYmFsYW5jZWQgPC0gUk9TRShsYWJlbCB+IC4sIGRhdGEgPSBkYXRfdHJhaW4sIHNlZWQ9MjAyMCkkZGF0YQogICAgdG1fcmlkZ2VfdHJhaW4gPC0gc3lzdGVtLnRpbWUocmlkZ2VfY3ZfbW9kZWw8LXJpZGdlX3RyYWluKHRyYWluX2RhdGE9ZGF0X3RyYWluX3JlYmFsYW5jZWQsIGFscGhhPWFscGhhLCBLPUssIGxhbWJkYT1sYW1iZGEpKQogICAgc2F2ZShyaWRnZV9jdl9tb2RlbCwgZmlsZT0iLi4vb3V0cHV0L3JpZGdlX2N2X21vZGVsLlJEYXRhIikKICAgIHNhdmUodG1fcmlkZ2VfdHJhaW4sIGZpbGU9Ii4uL291dHB1dC9yaWRnZV90cmFpbl90aW1lLlJEYXRhIikKICB9IGVsc2UgewogICAgbG9hZChmaWxlPSIuLi9vdXRwdXQvcmlkZ2VfY3ZfbW9kZWwuUkRhdGEiKQogICAgbG9hZChmaWxlPSIuLi9vdXRwdXQvcmlkZ2VfdHJhaW5fdGltZS5SRGF0YSIpCiAgfQp9CmBgYAoKKiBVc2UgQ3Jvc3MtVmFsaWRhdGlvbiB0byBDaG9vc2UgdGhlIE9wdGltYWwgTGFtYmRhIHdpdGggU21hbGxlc3QgTVNFCgpgYGB7cn0KaWYocnVuLnJpZGdlKXsKICBpZiAocnVuLmN2KXsKICAgIHNldC5zZWVkKDIwMjApCiAgICBkYXRfdHJhaW5fcmViYWxhbmNlZCA8LSBST1NFKGxhYmVsIH4gLiwgZGF0YSA9IGRhdF90cmFpbiwgc2VlZD0yMDIwKSRkYXRhCiAgICBmZWF0dXJlX3RyYWluID0gYXMubWF0cml4KGRhdF90cmFpbl9yZWJhbGFuY2VkWywtZGltKGRhdF90cmFpbl9yZWJhbGFuY2VkKVsyXV0pCiAgICBsYWJlbF90cmFpbiA9IGFzLmludGVnZXIoZGF0X3RyYWluX3JlYmFsYW5jZWQkbGFiZWwpCiAgICByaWRnZV9tb2RlbCA9IGN2LmdsbW5ldCh4PWZlYXR1cmVfdHJhaW4sIHk9bGFiZWxfdHJhaW4sIGFscGhhPWFscGhhLCBuZm9sZHM9SywgbGFtYmRhPWxhbWJkYSkKICAgIG9wdF9sYW1iZGEgPSByaWRnZV9tb2RlbCRsYW1iZGEubWluCiAgICBzYXZlKG9wdF9sYW1iZGEsIGZpbGU9Ii4uL291dHB1dC9yaWRnZV9vcHRpbWFsX2xhbWJkYS5SRGF0YSIpCiAgfSBlbHNlIHsKICAgIGxvYWQoZmlsZT0iLi4vb3V0cHV0L3JpZGdlX29wdGltYWxfbGFtYmRhLlJEYXRhIikKICB9Cn0KYGBgCgoqIFByZWRpY3Qgb24gVGVzdGluZyBTZXQgd2l0aCB0aGUgT3B0aW1hbCBMYW1iZGEKCmBgYHtyfQppZihydW4ucmlkZ2UpewogIHRtX3JpZGdlX3Rlc3QgPSBOQQogIGlmKHJ1bi50ZXN0KXsKICAgIGxvYWQoIi4uL291dHB1dC9yaWRnZV9jdl9tb2RlbC5SRGF0YSIpCiAgICBkYXRfdGVzdF9yZWJhbGFuY2VkIDwtIFJPU0UobGFiZWwgfiAuLCBkYXRhID0gZGF0X3Rlc3QsIHNlZWQ9MjAyMCkkZGF0YQogICAgZmVhdHVyZV90ZXN0IDwtIGFzLm1hdHJpeChkYXRfdGVzdF9yZWJhbGFuY2VkWywgLWRpbShkYXRfdGVzdF9yZWJhbGFuY2VkKVsyXV0pCiAgICB0bV9yaWRnZV90ZXN0IDwtIHN5c3RlbS50aW1lKGxhYmVsX3ByZWQ8LWFzLmludGVnZXIocmlkZ2VfdGVzdChtb2RlbD1yaWRnZV9jdl9tb2RlbCwgZmVhdHVyZXM9ZmVhdHVyZV90ZXN0LCBwcmVkLnR5cGUgPSAnY2xhc3MnKSkpCiAgICBzYXZlKHRtX3JpZGdlX3Rlc3QsIGZpbGU9Ii4uL291dHB1dC9yaWRnZV90ZXN0X3RpbWUuUkRhdGEiKQogIH0gZWxzZXsKICAgIGxvYWQoZmlsZT0iLi4vb3V0cHV0L3JpZGdlX3Rlc3RfdGltZS5SRGF0YSIpCiAgfQp9CmBgYAoKKiBTdW1tYXJpemUgUnVubmluZyBUaW1lCgpgYGB7cn0KaWYocnVuLnJpZGdlKXsKICBjYXQoIlRpbWUgZm9yIGNvbnN0cnVjdGluZyB0cmFpbmluZyBmZWF0dXJlcz0iLCB0bV9mZWF0dXJlX3RyYWluWzFdLCAicyBcbiIpCiAgY2F0KCJUaW1lIGZvciBjb25zdHJ1Y3RpbmcgdGVzdGluZyBmZWF0dXJlcz0iLCB0bV9mZWF0dXJlX3Rlc3RbMV0sICJzIFxuIikKICBjYXQoIlRpbWUgZm9yIHRyYWluaW5nIHJpZGdlIG1vZGVsPSIsIHRtX3JpZGdlX3RyYWluWzFdLCAicyBcbiIpIAogIGNhdCgiVGltZSBmb3IgdGVzdGluZyByaWRnZSBtb2RlbD0iLCB0bV9yaWRnZV90ZXN0WzFdLCAicyBcbiIpCn0KYGBgCgoqIEV2YWx1YXRpb24gb24gSW5kZXBlbmRlbnQgVGVzdGluZyBEYXRhIAoKYGBge3J9CmlmKHJ1bi5yaWRnZSl7CiAgbG9hZCgiLi4vb3V0cHV0L3JpZGdlX2N2X21vZGVsLlJEYXRhIikKICBkYXRfdGVzdF9yZWJhbGFuY2VkIDwtIFJPU0UobGFiZWwgfiAuLCBkYXRhID0gZGF0X3Rlc3QsIHNlZWQ9MjAyMCkkZGF0YQogIGZlYXR1cmVfdGVzdCA8LSBhcy5tYXRyaXgoZGF0X3Rlc3RfcmViYWxhbmNlZFssIC1kaW0oZGF0X3Rlc3RfcmViYWxhbmNlZClbMl1dKQogIGxhYmVsX3ByZWQgPSBhcy5pbnRlZ2VyKHByZWRpY3QocmlkZ2VfY3ZfbW9kZWwsIHM9b3B0X2xhbWJkYSwgbmV3eD1mZWF0dXJlX3Rlc3QsIHR5cGU9J2NsYXNzJykpCiAgbGFiZWxfdGVzdCA9IGFzLmludGVnZXIoZGF0X3Rlc3RfcmViYWxhbmNlZCRsYWJlbCkKICByaWRnZV9hY2N1cmFjeSA9IG1lYW4ocm91bmQobGFiZWxfdGVzdD09IGxhYmVsX3ByZWQpKQogIGNhdCgiVGhlIGFjY3VyYWN5IG9mIHRoZSByaWRnZSBtb2RlbCBpcyIsIHJpZGdlX2FjY3VyYWN5KjEwMCwgIiUuXG4iKQogIHJpZGdlX0FVQyA9IGF1Yyhyb2MobGFiZWxfcHJlZCxsYWJlbF90ZXN0KSkKICBjYXQoIlRoZSBBVUMgb2YgdGhlIHJpZGdlIG1vZGVsIGlzIiwgcmlkZ2VfQVVDLCAiLlxuIikKfQpgYGAKCi0tLS0tLS0tVEhJUyBJUyBUTyBTRVBBUkFURSBFQUNIIE1PREVMLiBUSElTIElTIFRPIFNFUEFSQVRFIEVBQ0ggTU9ERUwuIFRISVMgSVMgVE8gU0VQQVJBVEUgRUFDSCBNT0RFTC4tLS0tLS0tLS0tCgoKCgojIEFkdmFuY2VkIE1vZGVsIDU6IFBDQSArIExEQQoKKiBSZWJhbGFuY2UgVHJhaW5pbmcgU2V0CgpgYGB7cn0KaWYocnVuLnBjYV9sZGEpewogIGlmKHNhbXBsZS5yZXdlaWdodCl7CiAgICBkYXRfdHJhaW4kbGFiZWwgPC0gYXMuZmFjdG9yKGRhdF90cmFpbiRsYWJlbCkKICAgIGJhbGFuY2VkX3RyYWluX2RhdGEgPC0gU01PVEUobGFiZWx+LixkYXRhID0gZGF0X3RyYWluKQogICAgc2F2ZShiYWxhbmNlZF90cmFpbl9kYXRhLCBmaWxlPSIuLi9vdXRwdXQvZmVhdHVyZV9iYWxhbmNlZF90cmFpbi5SRGF0YSIpCiAgfSBlbHNlIHsKICAgIGxvYWQoYmFsYW5jZWRfdHJhaW5fZGF0YSwgZmlsZT0iLi4vb3V0cHV0L2ZlYXR1cmVfdHJhaW4uUkRhdGEiKQogIH0KfQpgYGAKCiogUGVyZm9ybSBQQ0EgZm9yIERpbWVuc2lvbiBSZWR1Y3Rpb24KCioqU2luY2UgdGhlcmUgYXJlIG92ZXIgNjAwMCBmZWF0dXJlcywgd2UgaW1wbGVtZW50IHRoZSBQQ0EgbWV0aG9kIHRvIHJlZHVjZSBkaW1lbnNpb24gYWNjb3JkaW5nIHRvIHRoZSBjb3ZhcmlhbmNlIG1hdHJpeC4gV2Ugb25seSByZXRhaW4gUENzIHdpdGggbGFyZ2UgdmFyaWFuY2UuKioKICAKYGBge3IgcGNhIGxkYX0KaWYocnVuLnBjYV9sZGEpewogIGJhbGFuY2VkX3Rlc3RfZGF0YSA8LSBkYXRfdGVzdAogIGlmKHJ1bi5zZWxlY3RfUEMpewogICAgI3NlcGFyYXRlIHRoZSBmZWF0dXJlcyBmcm9tIGxhYmVsCiAgICBkYXRfdHJhaW5fbmV3IDwtIGJhbGFuY2VkX3RyYWluX2RhdGFbLC1kaW0oYmFsYW5jZWRfdHJhaW5fZGF0YSlbMl1dCiAgICBkYXRfdGVzdF9uZXcgPC0gYmFsYW5jZWRfdGVzdF9kYXRhWywtZGltKGJhbGFuY2VkX3Rlc3RfZGF0YSlbMl1dCiAgICAjY3JlYXRlIGEgdmVjdG9yIGNvbnRhaW4gdGFyZ2V0IG51bWJlciBvZiBQQ3MKICAgIG51bS5wY2EgPC0gYygxMCw1MCw1MDAsMTAwMCkKICAgIHRyYWluX3BjYSA8LSBmdW5jdGlvbihudW0ucGNhKXsKICAgICAgZm9yKGkgaW4gMTpsZW5ndGgobnVtLnBjYSkpewogICAgICAgICNzdGFydCB0aW1lIGZvciB0cmFpbmluZyB0aGUgbW9kZWwKICAgICAgICB0cmFpbi5tb2RlbC5zdGFydCA9IHByb2MudGltZSgpCiAgICAgICAgI3J1biBQQ0EKICAgICAgICBwY2EgPC0gcHJjb21wKGRhdF90cmFpbl9uZXcpCiAgICAgICAgI3N0b3JlIGZvciBlYWNoIHBvdGVudGlhbCBQQwogICAgICAgIHRyYWluX3BjYSA8LSBkYXRhLmZyYW1lKHBjYSR4WywxOm51bS5wY2FbaV1dLCBsYWJlbCA9IGJhbGFuY2VkX3RyYWluX2RhdGFbZGltKGJhbGFuY2VkX3RyYWluX2RhdGEpWzJdXSkKICAgICAgICBwcmVkX3BjYSA8LSBwcmVkaWN0KHBjYSxkYXRfdGVzdF9uZXcpCiAgICAgICAgdGVzdF9wY2EgPC0gZGF0YS5mcmFtZShwcmVkX3BjYVssMTpudW0ucGNhW2ldXSwgbGFiZWwgPSBiYWxhbmNlZF90ZXN0X2RhdGFbZGltKGJhbGFuY2VkX3Rlc3RfZGF0YSlbMl1dKQogICAgICAgICNmaXR0aW5nIHRoZSBsZGEgbW9kZWwKICAgICAgICBsZGFfcGNhIDwtIGxkYShsYWJlbCB+IC4sIGRhdGEgPSB0cmFpbl9wY2EpIAogICAgICAgICNzdG9wIHRpbWUgZm9yIHRyYWluaW5nIHRoZSBtb2RlbAogICAgICAgIHRyYWluLm1vZGVsLmVuZCA9IHByb2MudGltZSgpCiAgICAgICAgI3N0YXJ0IHRpbWUgZm9yIHRlc3RpbmcgdGhlIG1vZGVsCiAgICAgICAgdGVzdC5tb2RlbC5zdGFydCA9IHByb2MudGltZSgpCiAgICAgICAgI3ByZWRpY3QgbGRhIG1vZGVsCiAgICAgICAgbGRhX3ByZWRfcGNhID0gcHJlZGljdChsZGFfcGNhLHRlc3RfcGNhWy1kaW0odGVzdF9wY2EpWzJdXSkKICAgICAgICAjZW5kIHRpbWUgZm9yIHRlc3RpbmcgdGhlIG1vZGVsCiAgICAgICAgdGVzdC5tb2RlbC5lbmQgPSBwcm9jLnRpbWUoKQogICAgICAgICN0ZXN0IGFjY3VyYWN5CiAgICAgICAgdGVzdF9hY2N1cmFjeT1jb25mdXNpb25NYXRyaXgobGRhX3ByZWRfcGNhJGNsYXNzLCB0ZXN0X3BjYSRsYWJlbCkkb3ZlcmFsbFsxXQogICAgICAgIHByaW50KGxpc3QobDE9dHJhaW4ubW9kZWwuZW5kIC0gdHJhaW4ubW9kZWwuc3RhcnQsCiAgICAgICAgICAgICAgICAgICBsMj10ZXN0Lm1vZGVsLmVuZCAtIHRlc3QubW9kZWwuc3RhcnQsCiAgICAgICAgICAgICAgICAgICBsMz10ZXN0X2FjY3VyYWN5KSkKICAgICAgfQogICAgfQogICAgdHJhaW5fcGNhKG51bS5wY2EpCiAgfQp9CmBgYAoKKipCeSBjb21wYXJpbmcgdGhlIHRyYWluaW5nIHRpbWUsIHRlc3QgdGltZSBhbmQgYWNjdXJhY3ksIHdlIHVzZSBtb2RlbCB3aXRoIDUwMCBQQ3MuKioKICAKICAqIE1vZGVsIFRyYWluaW5nCgpgYGB7ciBmaW5hbCBtb2RlbH0KaWYocnVuLnBjYV9sZGEpewogIHRyYWluLm1vZGVsLnN0YXJ0ID0gcHJvYy50aW1lKCkKICBpZihydW4ubGRhLnRyYWluKXsKICAgIHBjYV81MDAgPC0gcHJjb21wKGJhbGFuY2VkX3RyYWluX2RhdGFbLC1kaW0oYmFsYW5jZWRfdHJhaW5fZGF0YSlbMl1dKQogICAgdHJhaW5fcGNhXzUwMCA8LSBkYXRhLmZyYW1lKHBjYV81MDAkeFssMTo1MDBdLCBsYWJlbCA9IGJhbGFuY2VkX3RyYWluX2RhdGFbZGltKGJhbGFuY2VkX3RyYWluX2RhdGEpWzJdXSkKICAgIHByZWRfcGNhXzUwMCA8LSBwcmVkaWN0KHBjYV81MDAsYmFsYW5jZWRfdGVzdF9kYXRhWywtZGltKGJhbGFuY2VkX3Rlc3RfZGF0YSlbMl1dKQogICAgdGVzdF9wY2FfNTAwIDwtIGRhdGEuZnJhbWUocHJlZF9wY2FfNTAwWywxOjUwMF0sIGxhYmVsID0gYmFsYW5jZWRfdGVzdF9kYXRhW2RpbShiYWxhbmNlZF90ZXN0X2RhdGEpWzJdXSkKICAgIHNhdmUodHJhaW5fcGNhXzUwMCwgZmlsZT0iLi4vb3V0cHV0L2ZlYXR1cmVfcGNhX3RyYWluLlJEYXRhIikKICAgIHNhdmUodGVzdF9wY2FfNTAwLCBmaWxlPSIuLi9vdXRwdXQvZmVhdHVyZV9wY2FfdGVzdC5SRGF0YSIpICAKICB9IGVsc2UgewogICAgbG9hZCh0cmFpbl9wY2FfNTAsIGZpbGU9Ii4uL291dHB1dC9mZWF0dXJlX3BjYV90cmFpbi5SRGF0YSIpCiAgICBsb2FkKHRlc3RfcGNhXzUwLCBmaWxlPSIuLi9vdXRwdXQvZmVhdHVyZV9wY2FfdGVzdC5SRGF0YSIpICAKICB9CiAgI2NhbGN1bGF0ZSB0aGUgdHJhaW5pbmcgdGltZQogIGxkYV9wY2FfNTAgPC0gbGRhKGxhYmVsIH4gLiwgZGF0YSA9IHRyYWluX3BjYV81MCwgY3YgPSBUUlVFKQogIHRyYWluLm1vZGVsLmVuZCA9IHByb2MudGltZSgpCn0KYGBgCgoqIENhbGN1bGF0ZSB0aGUgVHJhaW5pbmcgYW5kIFRlc3RpbmcgQWNjdXJhY3kgb2YgTERBIE1vZGVsCgpgYGB7cn0KaWYocnVuLnBjYV9sZGEpewogIHRlc3QubW9kZWwuc3RhcnQgPSBwcm9jLnRpbWUoKQogIHByZWRfdHJhaW5fbGRhIDwtIHByZWRpY3QobGRhX3BjYV8xMCwgdHJhaW5fcGNhXzEwWy1kaW0odHJhaW5fcGNhXzEwKVsyXV0pCiAgYWNjdV90cmFpbl9sZGEgPC0gbWVhbihwcmVkX3RyYWluX2xkYSRjbGFzcyA9PSB0cmFpbl9wY2FfMTAkbGFiZWwpCiAgY2F0KCJUaGUgdHJhaW5pZyBhY2N1cmFjeSBvZiBtb2RlbDogTERBIiwgImlzIiwgYWNjdV90cmFpbl9sZGEqMTAwLCAiJS5cbiIpCiAgI2NhbGN1bGF0aW5nIHRoZSB0ZXN0IHRpbWUKICBpZihydW4udGVzdCl7CiAgICBwcmVkX3Rlc3RfbGRhIDwtIHByZWRpY3QobGRhX3BjYV8xMCwgdGVzdF9wY2FfMTApCiAgfQogIHRlc3QubW9kZWwuZW5kID0gcHJvYy50aW1lKCkKICBzYXZlKHByZWRfdGVzdF9sZGEsIGZpbGU9Ii4uL291dHB1dC9maXRfdHJhaW4uUkRhdGEiKQogIGFjY3VfdGVzdF9sZGEgPC0gbWVhbihwcmVkX3Rlc3RfbGRhJGNsYXNzID09IHRlc3RfcGNhXzEwJGxhYmVsKQogIGNhdCgiVGhlIGFjY3VyYWN5IG9mIG1vZGVsOiBMREEiLCAiaXMiLCBhY2N1X3Rlc3RfbGRhKjEwMCwgIiUuXG4iKQogIHRwci5mcHIgPC0gV2VpZ2h0ZWRST0MoYXMubnVtZXJpYyhwcmVkX3Rlc3RfbGRhJGNsYXNzKSwgdGVzdF9wY2FfMTAkbGFiZWwpCiAgbGRhX2F1YyA9IFdlaWdodGVkQVVDKHRwci5mcHIpCiAgY2F0KCJUaGUgQVVDIG9mIG1vZGVsOiBMREEgaXMiLCBsZGFfYXVjLCAiLlxuIikKfQpgYGAKCiogU3VtbWFyaXplIFJ1bm5pbmcgVGltZQoKUHJlZGljdGlvbiBwZXJmb3JtYW5jZSBtYXR0ZXJzLCBzbyBkb2VzIHRoZSBydW5uaW5nIHRpbWVzIGZvciBjb25zdHJ1Y3RpbmcgZmVhdHVyZXMgYW5kIGZvciB0cmFpbmluZyB0aGUgbW9kZWwsIGVzcGVjaWFsbHkgd2hlbiB0aGUgY29tcHV0YXRpb24gcmVzb3VyY2UgaXMgbGltaXRlZC4gCgpgYGB7cn0KaWYocnVuLnBjYV9sZGEpewogIHRtX3RyYWluIDwtIHRyYWluLm1vZGVsLmVuZCAtIHRyYWluLm1vZGVsLnN0YXJ0CiAgdG1fdGVzdCA8LSB0ZXN0Lm1vZGVsLmVuZCAtIHRlc3QubW9kZWwuc3RhcnQKICBjYXQoIlRpbWUgZm9yIGNvbnN0cnVjdGluZyB0cmFpbmluZyBmZWF0dXJlcz0iLCB0bV9mZWF0dXJlX3RyYWluWzFdLCAicyBcbiIpCiAgY2F0KCJUaW1lIGZvciBjb25zdHJ1Y3RpbmcgdGVzdGluZyBmZWF0dXJlcz0iLCB0bV9mZWF0dXJlX3Rlc3RbMV0sICJzIFxuIikKICBjYXQoIlRpbWUgZm9yIHRyYWluaW5nIG1vZGVsPSIsIHRtX3RyYWluWzFdLCAicyBcbiIpIAogIGNhdCgiVGltZSBmb3IgdGVzdGluZyBtb2RlbD0iLCB0bV90ZXN0WzFdLCAicyBcbiIpCn0KYGBgCgoqIEdlbmVyYXRlIGEgY3N2IG9uIHByZXNlbnRhdGlvbiBkYXkKCmBgYHtyIGdlbmVyYXRlIGNzdn0KaWYgKHJ1bi5wcmVzZW50YXRpb24uZGF5KXsKICBjc3ZmaWxlb3V0cHV0PC0iLi4vb3V0cHV0L2xhYmVsX3ByZWRpY3Rpb24uY3N2IgogIEFkdmFuY2VkPC1zdm1fcmJmX3ByZWQKICBCYXNlbGluZTwtbGFiZWxfcHJlZF9iYXNlbGluZQogIEluZGV4PC10ZXN0X2lkeAogIGNzdmRhdGEgPC0gZGF0YS5mcmFtZShJbmRleCwgQmFzZWxpbmUsIEFkdmFuY2VkKQogIAogIHdyaXRlLmNzdihjc3ZkYXRhLGNzdmZpbGVvdXRwdXQsIHJvdy5uYW1lcz1GQUxTRSxxdW90ZSA9IEZBTFNFKQogIAogIAogIHByZXNlbnRhdGlvbl9kYXlfZ2JtX2FjY3UgPC0gbWVhbihpbmZvJGxhYmVsID09IGxhYmVsX3ByZWRfYmFzZWxpbmUpCiAgY2F0KCJUaGUgYWNjdXJhY3kgb2YgR0JNIGJhc2VsaW5lIG1vZGVsIGlzIiwgcHJlc2VudGF0aW9uX2RheV9nYm1fYWNjdSoxMDAsICIlLlxuIikKICAKICBwcmVzZW50YXRpb25fZGF5X2FkdmFuY2VkX2FjY3UgPC0gbWVhbihpbmZvJGxhYmVsID09IHN2bV9yYmZfcHJlZCkKICBjYXQoIlRoZSBhY2N1cmFjeSBvZiBBZHZhbmNlZCBtb2RlbCBpcyIsIHByZXNlbnRhdGlvbl9kYXlfYWR2YW5jZWRfYWNjdSoxMDAsICIlLlxuIikKICAKfQpgYGAKCiMjIyBSZWZlcmVuY2UKLSBEdSwgUy4sIFRhbywgWS4sICYgTWFydGluZXosIEEuIE0uICgyMDE0KS4gQ29tcG91bmQgZmFjaWFsIGV4cHJlc3Npb25zIG9mIGVtb3Rpb24uIFByb2NlZWRpbmdzIG9mIHRoZSBOYXRpb25hbCBBY2FkZW15IG9mIFNjaWVuY2VzLCAxMTEoMTUpLCBFMTQ1NC1FMTQ2Mi4=